Panels you can see: the hub's palette by default, clear surface steps and a 1 dp edge on every panel (closes #24) 840c75813f86
Cards were near-black on black on a phone: the app always used the wallpaper's dynamic colours, whose dark container tones sit a shade apart, and panels had no edge. Now OxygenHub's palette by default (Account › Wallpaper colours turns Android's on), with dark containers a clear step apart and brighter edges in both; every card is a Panel (an outlined card on surfaceContainerLow whose contents show its tone); wallpaper colours in the dark are lifted the same way. Backdrops clip to their bounds, and Markdown images the app cannot show take no room instead of a 180 dp placeholder.
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 4b52d4cb73d0
- When
- · 2026-10-28 16:12 UTC
- Signature
- ◈ sealed by agent:claude@andymccall
- Hash
840c75813f8616e0bb4ad687ad0cfd9e57feef640579e2ce706f9ddd838399ad- Depends on
- 144cbf7bf2b5 1dfa631436b6 206d05a042e7 23279b95b5b2 2a15621eca66 31e4f3755627 40ab4df121e0 4aee5293461d 57c0824300db 98dbd1220929 af9f2e514c72 d1f635be9442 db00632a61d6 dfbc3e972872 e0004d61958c e2df27a81e5d e833eb0a9e7f ff65ea456ff1
- Depended on by
- 263c6cad5e51 c53e328b2db4 1823bc12eda1 821035db5a1c d98e5a147428
- In bookmarks
- main
- In tags
- v0.1.1 v0.1.2 v0.1.3 v0.1.4 v0.1.5 v0.1.6 v0.1.7 v0.1.8 v0.1.9
- Edits
- 69 atoms, 4 tree operations, 20 files
Browse the tree at this change
added app/src/main/java/io/oxygenhub/android/ui/common/MarkdownImages.kt +20 −0
| 1 | + | package io.oxygenhub.android.ui.common |
|
| 2 | + | ||
| 3 | + | import androidx.compose.runtime.Composable |
|
| 4 | + | import androidx.compose.ui.geometry.Size |
|
| 5 | + | import androidx.compose.ui.unit.Density |
|
| 6 | + | import com.mikepenz.markdown.model.ImageData |
|
| 7 | + | import com.mikepenz.markdown.model.ImageTransformer |
|
| 8 | + | import com.mikepenz.markdown.model.PlaceholderConfig |
|
| 9 | + | ||
| 10 | + | /** |
|
| 11 | + | * Images in Markdown (#24): the app fetches none yet, so each takes no room, |
|
| 12 | + | * where the renderer's default left a 180 dp blank (a README's badge, say). |
|
| 13 | + | */ |
|
| 14 | + | object NoImages : ImageTransformer { |
|
| 15 | + | @Composable |
|
| 16 | + | override fun transform(link: String): ImageData? = null |
|
| 17 | + | ||
| 18 | + | override fun placeholderConfig(density: Density, containerSize: Size, intrinsicImageSize: Size): PlaceholderConfig = |
|
| 19 | + | PlaceholderConfig(Size.Zero) |
|
| 20 | + | } |
added app/src/main/java/io/oxygenhub/android/ui/common/Panel.kt +35 −0
| 1 | + | package io.oxygenhub.android.ui.common |
|
| 2 | + | ||
| 3 | + | import androidx.compose.foundation.BorderStroke |
|
| 4 | + | import androidx.compose.foundation.layout.ColumnScope |
|
| 5 | + | import androidx.compose.material3.CardColors |
|
| 6 | + | import androidx.compose.material3.CardDefaults |
|
| 7 | + | import androidx.compose.material3.MaterialTheme |
|
| 8 | + | import androidx.compose.material3.OutlinedCard |
|
| 9 | + | import androidx.compose.runtime.Composable |
|
| 10 | + | import androidx.compose.ui.Modifier |
|
| 11 | + | import androidx.compose.ui.graphics.Color |
|
| 12 | + | import androidx.compose.ui.unit.dp |
|
| 13 | + | ||
| 14 | + | /** |
|
| 15 | + | * A panel you can see on any screen (#24): Material's outlined card on a |
|
| 16 | + | * tone a clear step above the background, with a 1 dp edge, as the hub's |
|
| 17 | + | * pages give theirs. Every card in the app is one. |
|
| 18 | + | */ |
|
| 19 | + | @Composable |
|
| 20 | + | fun Panel(modifier: Modifier = Modifier, colors: CardColors = panelColors(), content: @Composable ColumnScope.() -> Unit) { |
|
| 21 | + | OutlinedCard(modifier, colors = colors, border = panelBorder()) { |
|
| 22 | + | // What sits on a panel shows the panel: a ListItem, say, paints |
|
| 23 | + | // `surface`, which is the screen's own tone. |
|
| 24 | + | val scope = this |
|
| 25 | + | MaterialTheme(colorScheme = MaterialTheme.colorScheme.copy(surface = Color.Transparent)) { scope.content() } |
|
| 26 | + | } |
|
| 27 | + | } |
|
| 28 | + | ||
| 29 | + | /** A panel's colours; `alpha` below 1 lets a backdrop show through. */ |
|
| 30 | + | @Composable |
|
| 31 | + | fun panelColors(alpha: Float = 1f): CardColors = |
|
| 32 | + | CardDefaults.outlinedCardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = alpha)) |
|
| 33 | + | ||
| 34 | + | @Composable |
|
| 35 | + | fun panelBorder(): BorderStroke = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant) |
added app/src/main/java/io/oxygenhub/android/ui/theme/Appearance.kt +37 −0
| 1 | + | package io.oxygenhub.android.ui.theme |
|
| 2 | + | ||
| 3 | + | import android.content.Context |
|
| 4 | + | import androidx.compose.runtime.Composable |
|
| 5 | + | import androidx.compose.runtime.collectAsState |
|
| 6 | + | import androidx.compose.runtime.getValue |
|
| 7 | + | import androidx.compose.ui.platform.LocalContext |
|
| 8 | + | import kotlinx.coroutines.flow.MutableStateFlow |
|
| 9 | + | import kotlinx.coroutines.flow.StateFlow |
|
| 10 | + | import kotlinx.coroutines.flow.asStateFlow |
|
| 11 | + | ||
| 12 | + | /** |
|
| 13 | + | * Whether the app takes its colours from the wallpaper (#24). Off by |
|
| 14 | + | * default: the hub's own palette, as oxygenhub.io and its backdrops have. |
|
| 15 | + | */ |
|
| 16 | + | object Appearance { |
|
| 17 | + | private const val PREFS = "appearance" |
|
| 18 | + | private const val WALLPAPER = "wallpaper_colours" |
|
| 19 | + | private var state: MutableStateFlow<Boolean>? = null |
|
| 20 | + | ||
| 21 | + | fun wallpaperColours(context: Context): StateFlow<Boolean> = |
|
| 22 | + | (state ?: MutableStateFlow(prefs(context).getBoolean(WALLPAPER, false)).also { state = it }).asStateFlow() |
|
| 23 | + | ||
| 24 | + | fun setWallpaperColours(context: Context, on: Boolean) { |
|
| 25 | + | prefs(context).edit().putBoolean(WALLPAPER, on).apply() |
|
| 26 | + | wallpaperColours(context) |
|
| 27 | + | state?.value = on |
|
| 28 | + | } |
|
| 29 | + | ||
| 30 | + | private fun prefs(context: Context) = context.applicationContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE) |
|
| 31 | + | } |
|
| 32 | + | ||
| 33 | + | @Composable |
|
| 34 | + | fun rememberWallpaperColours(): Boolean { |
|
| 35 | + | val on by Appearance.wallpaperColours(LocalContext.current).collectAsState() |
|
| 36 | + | return on |
|
| 37 | + | } |
added app/src/test/java/io/oxygenhub/android/ui/ThemeTest.kt +30 −0
| 1 | + | package io.oxygenhub.android.ui |
|
| 2 | + | ||
| 3 | + | import androidx.compose.material3.darkColorScheme |
|
| 4 | + | import androidx.compose.ui.graphics.Color |
|
| 5 | + | import androidx.compose.ui.graphics.luminance |
|
| 6 | + | import io.oxygenhub.android.ui.theme.lifted |
|
| 7 | + | import org.junit.Assert.assertTrue |
|
| 8 | + | import org.junit.Test |
|
| 9 | + | ||
| 10 | + | class ThemeTest { |
|
| 11 | + | @Test |
|
| 12 | + | fun wallpaperColoursInTheDarkLiftEachContainerAStepAboveTheLast() { |
|
| 13 | + | // Android's dark tones a shade apart, as a near-black wallpaper gives them. |
|
| 14 | + | val near = Color(0xFF121212) |
|
| 15 | + | val s = lifted( |
|
| 16 | + | darkColorScheme( |
|
| 17 | + | background = Color(0xFF0F0F0F), |
|
| 18 | + | onSurface = Color(0xFFE6E6E6), |
|
| 19 | + | surfaceContainerLow = near, |
|
| 20 | + | surfaceContainer = near, |
|
| 21 | + | surfaceContainerHigh = near, |
|
| 22 | + | surfaceContainerHighest = near, |
|
| 23 | + | ), |
|
| 24 | + | ) |
|
| 25 | + | val steps = listOf(s.background, s.surfaceContainerLow, s.surfaceContainer, s.surfaceContainerHigh, s.surfaceContainerHighest, s.outlineVariant) |
|
| 26 | + | .map { it.luminance() } |
|
| 27 | + | assertTrue("each above the last: $steps", steps.zipWithNext().all { (a, b) -> b > a }) |
|
| 28 | + | assertTrue("a panel's edge stands well off the background: $steps", steps.last() > steps.first() * 3) |
|
| 29 | + | } |
|
| 30 | + | } |
modified CHANGELOG.md +4 −0
| 1 | 1 | # Changelog |
|
| 2 | 2 | ||
| 3 | + | ## Unreleased |
|
| 4 | + | ||
| 5 | + | - **Panels you can see (#24):** on a phone, cards and boxes were near-black on black. The app now uses OxygenHub's own colours by default (Account › Wallpaper colours turns Android's back on), every card is one panel with a 1 dp edge, as the website's have, on a tone a clear step above the background, and wallpaper colours in the dark get the same steps. A backdrop stays inside its band, and an image the app cannot show yet (a README's badge) no longer leaves a 180 dp blank. |
|
| 6 | + | ||
| 3 | 7 | ## 0.1.0 (2026-10-28) |
|
| 4 | 8 | ||
| 5 | 9 | - **The release's secrets checked in seconds (#7):** `oh run start --job release-check` runs `scripts/release-check.sh` with no build: the keystore's SHA-256 and the entry its password opens (to compare with the keystore you hold), that the key `oxygenhub` opens with that password as signing needs, and that Firebase's settings are whole JSON; never a key, a password or the settings. The release job runs the same check first. |
|
| … | 24 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/alerts/LandAlertsCard.kt +2 −2
| … | 9 unchanged lines |
||
| 10 | 10 | import androidx.compose.foundation.layout.Row |
|
| 11 | 11 | import androidx.compose.foundation.layout.fillMaxWidth |
|
| 12 | 12 | import androidx.compose.foundation.layout.padding |
|
| 13 | - | import androidx.compose.material3.ElevatedCard |
|
| 14 | 13 | import androidx.compose.material3.MaterialTheme |
|
| 15 | 14 | import androidx.compose.material3.Switch |
|
| 16 | 15 | import androidx.compose.material3.Text |
|
| … | 6 unchanged lines |
||
| 23 | 22 | import androidx.compose.ui.unit.dp |
|
| 24 | 23 | import androidx.core.content.ContextCompat |
|
| 25 | 24 | import androidx.lifecycle.compose.collectAsStateWithLifecycle |
|
| 25 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 26 | 26 | ||
| 27 | 27 | /** On the Account screen: notifications when a proposal can be landed (#20), on or off. */ |
|
| 28 | 28 | @Composable |
|
| … | 10 unchanged lines |
||
| 39 | 39 | ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED |
|
| 40 | 40 | if (granted) LandAlerts.enable(context) else ask.launch(Manifest.permission.POST_NOTIFICATIONS) |
|
| 41 | 41 | } |
|
| 42 | - | ElevatedCard(Modifier.fillMaxWidth()) { |
|
| 42 | + | Panel(Modifier.fillMaxWidth()) { |
|
| 43 | 43 | Row(Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp)) { |
|
| 44 | 44 | Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) { |
|
| 45 | 45 | Text("Ready to land", style = MaterialTheme.typography.titleMedium) |
|
| … | 14 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/runner/RunnerCard.kt +2 −2
| … | 10 unchanged lines |
||
| 11 | 11 | import androidx.compose.foundation.layout.fillMaxWidth |
|
| 12 | 12 | import androidx.compose.foundation.layout.padding |
|
| 13 | 13 | import androidx.compose.material3.Button |
|
| 14 | - | import androidx.compose.material3.ElevatedCard |
|
| 15 | 14 | import androidx.compose.material3.MaterialTheme |
|
| 16 | 15 | import androidx.compose.material3.OutlinedButton |
|
| 17 | 16 | import androidx.compose.material3.Text |
|
| … | 11 unchanged lines |
||
| 29 | 28 | import androidx.lifecycle.compose.collectAsStateWithLifecycle |
|
| 30 | 29 | import io.oxygenhub.android.api.HubApi |
|
| 31 | 30 | import io.oxygenhub.android.api.userMessage |
|
| 31 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 32 | 32 | import io.oxygenhub.android.ui.theme.LocalStatusColors |
|
| 33 | 33 | import kotlinx.coroutines.CancellationException |
|
| 34 | 34 | import kotlinx.coroutines.launch |
|
| … | 19 unchanged lines |
||
| 54 | 54 | if (granted) PhoneRunner.start(context) else ask.launch(Manifest.permission.POST_NOTIFICATIONS) |
|
| 55 | 55 | } |
|
| 56 | 56 | val status = LocalStatusColors.current |
|
| 57 | - | ElevatedCard(Modifier.fillMaxWidth()) { |
|
| 57 | + | Panel(Modifier.fillMaxWidth()) { |
|
| 58 | 58 | Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { |
|
| 59 | 59 | Text("OxygenFlow runner", style = MaterialTheme.typography.titleMedium) |
|
| 60 | 60 | val (line, colour) = when (val s = state) { |
|
| … | 46 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/account/AccountScreen.kt +25 −4
| … | 15 unchanged lines |
||
| 16 | 16 | import androidx.compose.material.icons.outlined.Dns |
|
| 17 | 17 | import androidx.compose.material.icons.outlined.Key |
|
| 18 | 18 | import androidx.compose.material.icons.outlined.Memory |
|
| 19 | + | import androidx.compose.material.icons.outlined.Palette |
|
| 19 | 20 | import androidx.compose.material3.AlertDialog |
|
| 20 | - | import androidx.compose.material3.Card |
|
| 21 | 21 | import androidx.compose.material3.CircularProgressIndicator |
|
| 22 | 22 | import androidx.compose.material3.Icon |
|
| 23 | 23 | import androidx.compose.material3.ListItem |
|
| 24 | 24 | import androidx.compose.material3.MaterialTheme |
|
| 25 | 25 | import androidx.compose.material3.OutlinedButton |
|
| 26 | + | import androidx.compose.material3.Switch |
|
| 26 | 27 | import androidx.compose.material3.Text |
|
| 27 | 28 | import androidx.compose.material3.TextButton |
|
| 28 | 29 | import androidx.compose.runtime.Composable |
|
| … | 4 unchanged lines |
||
| 33 | 34 | import androidx.compose.runtime.setValue |
|
| 34 | 35 | import androidx.compose.ui.Alignment |
|
| 35 | 36 | import androidx.compose.ui.Modifier |
|
| 37 | + | import androidx.compose.ui.platform.LocalContext |
|
| 36 | 38 | import androidx.compose.ui.text.font.FontWeight |
|
| 37 | 39 | import androidx.compose.ui.unit.dp |
|
| 38 | - | import io.oxygenhub.android.auth.Session |
|
| 40 | + | import io.oxygenhub.android.alerts.LandAlertsCard |
|
| 39 | 41 | import io.oxygenhub.android.api.Avatar |
|
| 40 | 42 | import io.oxygenhub.android.api.HubApi |
|
| 41 | - | import io.oxygenhub.android.alerts.LandAlertsCard |
|
| 43 | + | import io.oxygenhub.android.auth.Session |
|
| 42 | 44 | import io.oxygenhub.android.runner.RunnerCard |
|
| 43 | 45 | import io.oxygenhub.android.ui.common.AppScreen |
|
| 44 | 46 | import io.oxygenhub.android.ui.common.AvatarImage |
|
| 47 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 48 | + | import io.oxygenhub.android.ui.theme.Appearance |
|
| 49 | + | import io.oxygenhub.android.ui.theme.rememberWallpaperColours |
|
| 45 | 50 | import io.oxygenhub.android.update.UpdateCard |
|
| 46 | 51 | import kotlinx.coroutines.launch |
|
| 47 | 52 | ||
| … | 19 unchanged lines |
||
| 67 | 72 | LandAlertsCard() |
|
| 68 | 73 | RunnerCard(api, session.hub, session.canWrite) |
|
| 69 | 74 | UpdateCard(api, session.hub) |
|
| 70 | - | Card(Modifier.fillMaxWidth()) { |
|
| 75 | + | AppearanceCard() |
|
| 76 | + | Panel(Modifier.fillMaxWidth()) { |
|
| 71 | 77 | ListItem( |
|
| 72 | 78 | leadingContent = { Icon(Icons.Outlined.Dns, contentDescription = null) }, |
|
| 73 | 79 | headlineContent = { Text("Hub") }, |
|
| … | 42 unchanged lines |
||
| 116 | 122 | }) { Text("Sign out") } |
|
| 117 | 123 | }, |
|
| 118 | 124 | dismissButton = { TextButton(onClick = { confirming = false }) { Text("Cancel") } }, |
|
| 125 | + | ) |
|
| 126 | + | } |
|
| 127 | + | } |
|
| 128 | + | ||
| 129 | + | /** The app's colours (#24): the hub's palette, or the wallpaper's. */ |
|
| 130 | + | @Composable |
|
| 131 | + | private fun AppearanceCard() { |
|
| 132 | + | val context = LocalContext.current |
|
| 133 | + | val wallpaper = rememberWallpaperColours() |
|
| 134 | + | Panel(Modifier.fillMaxWidth()) { |
|
| 135 | + | ListItem( |
|
| 136 | + | leadingContent = { Icon(Icons.Outlined.Palette, contentDescription = null) }, |
|
| 137 | + | headlineContent = { Text("Wallpaper colours") }, |
|
| 138 | + | supportingContent = { Text(if (wallpaper) "The app takes its colours from your wallpaper." else "The app uses OxygenHub's own colours, as the website does.") }, |
|
| 139 | + | trailingContent = { Switch(checked = wallpaper, onCheckedChange = { Appearance.setWallpaperColours(context, it) }) }, |
|
| 119 | 140 | ) |
|
| 120 | 141 | } |
|
| 121 | 142 | } |
modified app/src/main/java/io/oxygenhub/android/ui/backdrop/Backdrop.kt +3 −0
| … | 24 unchanged lines |
||
| 25 | 25 | import androidx.compose.runtime.setValue |
|
| 26 | 26 | import androidx.compose.ui.Modifier |
|
| 27 | 27 | import androidx.compose.ui.draw.alpha |
|
| 28 | + | import androidx.compose.ui.draw.clipToBounds |
|
| 28 | 29 | import androidx.compose.ui.geometry.Offset |
|
| 29 | 30 | import androidx.compose.ui.graphics.Brush |
|
| 30 | 31 | import androidx.compose.ui.graphics.Color |
|
| … | 80 unchanged lines |
||
| 111 | 112 | ||
| 112 | 113 | Box( |
|
| 113 | 114 | modifier |
|
| 115 | + | // A mark near the edge stays in its band (#24), never on the content around it. |
|
| 116 | + | .clipToBounds() |
|
| 114 | 117 | .onSizeChanged { size = it } |
|
| 115 | 118 | .pointerInput(scene) { |
|
| 116 | 119 | awaitPointerEventScope { |
|
| … | 140 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/code/CodeBrowser.kt +4 −3
| … | 18 unchanged lines |
||
| 19 | 19 | import androidx.compose.material3.AssistChip |
|
| 20 | 20 | import androidx.compose.material3.DropdownMenu |
|
| 21 | 21 | import androidx.compose.material3.DropdownMenuItem |
|
| 22 | - | import androidx.compose.material3.ElevatedCard |
|
| 23 | 22 | import androidx.compose.material3.HorizontalDivider |
|
| 24 | 23 | import androidx.compose.material3.Icon |
|
| 25 | 24 | import androidx.compose.material3.ListItem |
|
| … | 14 unchanged lines |
||
| 40 | 39 | import io.oxygenhub.android.ui.common.EmptyState |
|
| 41 | 40 | import io.oxygenhub.android.ui.common.Load |
|
| 42 | 41 | import io.oxygenhub.android.ui.common.LoadContent |
|
| 42 | + | import io.oxygenhub.android.ui.common.NoImages |
|
| 43 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 43 | 44 | ||
| 44 | 45 | /** |
|
| 45 | 46 | * A repository's code (#3): where you are (the reference, then each folder |
|
| … | 31 unchanged lines |
||
| 77 | 78 | } |
|
| 78 | 79 | dir.readme?.let { readme -> |
|
| 79 | 80 | item { |
|
| 80 | - | ElevatedCard(Modifier.fillMaxWidth().padding(12.dp)) { |
|
| 81 | + | Panel(Modifier.fillMaxWidth().padding(12.dp)) { |
|
| 81 | 82 | Text( |
|
| 82 | 83 | readme.path.substringAfterLast('/'), |
|
| 83 | 84 | style = MaterialTheme.typography.titleSmall, |
|
| … | 1 unchanged lines |
||
| 85 | 86 | ) |
|
| 86 | 87 | Box(Modifier.padding(16.dp)) { |
|
| 87 | 88 | if (readme.path.lowercase().let { it.endsWith(".md") || it.endsWith(".markdown") }) { |
|
| 88 | - | Markdown(readme.text) |
|
| 89 | + | Markdown(readme.text, imageTransformer = NoImages) |
|
| 89 | 90 | } else { |
|
| 90 | 91 | Text(readme.text, style = MaterialTheme.typography.bodyMedium) |
|
| 91 | 92 | } |
|
| … | 96 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/code/CodeScreens.kt +4 −3
| … | 12 unchanged lines |
||
| 13 | 13 | import androidx.compose.foundation.layout.width |
|
| 14 | 14 | import androidx.compose.foundation.lazy.LazyColumn |
|
| 15 | 15 | import androidx.compose.foundation.lazy.LazyListScope |
|
| 16 | + | import androidx.compose.foundation.lazy.items |
|
| 16 | 17 | import androidx.compose.foundation.lazy.itemsIndexed |
|
| 17 | - | import androidx.compose.foundation.lazy.items |
|
| 18 | 18 | import androidx.compose.foundation.rememberScrollState |
|
| 19 | 19 | import androidx.compose.foundation.text.selection.SelectionContainer |
|
| 20 | 20 | import androidx.compose.material.icons.Icons |
|
| … | 27 unchanged lines |
||
| 48 | 48 | import io.oxygenhub.android.api.FileChange |
|
| 49 | 49 | import io.oxygenhub.android.ui.common.AppScreen |
|
| 50 | 50 | import io.oxygenhub.android.ui.common.LoadContent |
|
| 51 | + | import io.oxygenhub.android.ui.common.NoImages |
|
| 51 | 52 | import io.oxygenhub.android.ui.common.relativeTime |
|
| 52 | 53 | import io.oxygenhub.android.ui.theme.LocalStatusColors |
|
| 53 | 54 | ||
| … | 19 unchanged lines |
||
| 73 | 74 | } |
|
| 74 | 75 | } |
|
| 75 | 76 | if (vm.isMarkdown && rendered) { |
|
| 76 | - | LazyColumn { item { Box(Modifier.padding(16.dp)) { Markdown(text) } } } |
|
| 77 | + | LazyColumn { item { Box(Modifier.padding(16.dp)) { Markdown(text, imageTransformer = NoImages) } } } |
|
| 77 | 78 | } else { |
|
| 78 | 79 | CodeView(text, vm.path) |
|
| 79 | 80 | } |
|
| … | 81 unchanged lines |
||
| 161 | 162 | color = MaterialTheme.colorScheme.onSurfaceVariant, |
|
| 162 | 163 | ) |
|
| 163 | 164 | val rest = d.change.message.lines().drop(1).joinToString("\n").trim() |
|
| 164 | - | if (rest.isNotEmpty()) Markdown(rest) |
|
| 165 | + | if (rest.isNotEmpty()) Markdown(rest, imageTransformer = NoImages) |
|
| 165 | 166 | val where = d.inBookmarks.map { it } + d.inTags.map { "tag $it" } |
|
| 166 | 167 | if (where.isNotEmpty()) Text("In " + where.joinToString(", "), style = MaterialTheme.typography.bodySmall) |
|
| 167 | 168 | } |
|
| … | 112 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/dashboard/DashboardScreen.kt +5 −9
| … | 17 unchanged lines |
||
| 18 | 18 | import androidx.compose.material.icons.outlined.Inbox |
|
| 19 | 19 | import androidx.compose.material.icons.outlined.MarkEmailUnread |
|
| 20 | 20 | import androidx.compose.material.icons.outlined.Search |
|
| 21 | - | import androidx.compose.material3.Card |
|
| 22 | - | import androidx.compose.material3.CardDefaults |
|
| 23 | 21 | import androidx.compose.material3.HorizontalDivider |
|
| 24 | 22 | import androidx.compose.material3.Icon |
|
| 25 | 23 | import androidx.compose.material3.IconButton |
|
| … | 20 unchanged lines |
||
| 46 | 44 | import io.oxygenhub.android.ui.common.AvatarImage |
|
| 47 | 45 | import io.oxygenhub.android.ui.common.Load |
|
| 48 | 46 | import io.oxygenhub.android.ui.common.LoadContent |
|
| 47 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 48 | + | import io.oxygenhub.android.ui.common.panelColors |
|
| 49 | 49 | import io.oxygenhub.android.ui.common.relativeTime |
|
| 50 | 50 | import io.oxygenhub.android.ui.repos.RepositoryRow |
|
| 51 | 51 | import io.oxygenhub.android.ui.theme.LocalStatusColors |
|
| … | 83 unchanged lines |
||
| 135 | 135 | ||
| 136 | 136 | @Composable |
|
| 137 | 137 | private fun ProposalCard(title: String, list: List<ProposalBrief>, onOpen: (ProposalBrief) -> Unit) { |
|
| 138 | - | Card(Modifier.fillMaxWidth(), colors = glass()) { |
|
| 138 | + | Panel(Modifier.fillMaxWidth(), colors = panelColors(alpha = 0.9f)) { |
|
| 139 | 139 | Text( |
|
| 140 | 140 | "$title · ${list.size}", |
|
| 141 | 141 | style = MaterialTheme.typography.titleMedium, |
|
| … | 24 unchanged lines |
||
| 166 | 166 | ||
| 167 | 167 | @Composable |
|
| 168 | 168 | private fun Unread(inbox: Load<Inbox>, onInbox: () -> Unit) { |
|
| 169 | - | Card(Modifier.fillMaxWidth().clickable(onClick = onInbox), colors = glass()) { |
|
| 169 | + | Panel(Modifier.fillMaxWidth().clickable(onClick = onInbox), colors = panelColors(alpha = 0.9f)) { |
|
| 170 | 170 | ListItem( |
|
| 171 | 171 | colors = ListItemDefaults.colors(containerColor = Color.Transparent), |
|
| 172 | 172 | leadingContent = { |
|
| … | 19 unchanged lines |
||
| 192 | 192 | ||
| 193 | 193 | @Composable |
|
| 194 | 194 | private fun Recent(d: Dashboard, onOpenRepository: (Repository) -> Unit, onAllRepositories: () -> Unit) { |
|
| 195 | - | Card(Modifier.fillMaxWidth(), colors = glass()) { |
|
| 195 | + | Panel(Modifier.fillMaxWidth(), colors = panelColors(alpha = 0.9f)) { |
|
| 196 | 196 | Text( |
|
| 197 | 197 | if (d.recentAreYours) "Your recent repositories" else "Recently updated on the hub", |
|
| 198 | 198 | style = MaterialTheme.typography.titleMedium, |
|
| … | 17 unchanged lines |
||
| 216 | 216 | TextButton(onClick = onAllRepositories, modifier = Modifier.padding(8.dp)) { Text("All repositories") } |
|
| 217 | 217 | } |
|
| 218 | 218 | } |
|
| 219 | - | ||
| 220 | - | /** A card the sky shows through a little. */ |
|
| 221 | - | @Composable |
|
| 222 | - | private fun glass() = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = 0.8f)) |
modified app/src/main/java/io/oxygenhub/android/ui/issues/IssueScreen.kt +8 −7
| 1 | 1 | package io.oxygenhub.android.ui.issues |
|
| 2 | 2 | ||
| 3 | - | import androidx.compose.material.icons.outlined.PersonAdd |
|
| 4 | - | import androidx.compose.material.icons.outlined.Close |
|
| 5 | - | import androidx.compose.material.icons.outlined.Add |
|
| 6 | 3 | import androidx.compose.foundation.layout.Arrangement |
|
| 7 | 4 | import androidx.compose.foundation.layout.Column |
|
| 8 | 5 | import androidx.compose.foundation.layout.ExperimentalLayoutApi |
|
| … | 6 unchanged lines |
||
| 15 | 12 | import androidx.compose.foundation.lazy.items |
|
| 16 | 13 | import androidx.compose.material.icons.Icons |
|
| 17 | 14 | import androidx.compose.material.icons.automirrored.outlined.Label |
|
| 15 | + | import androidx.compose.material.icons.outlined.Add |
|
| 16 | + | import androidx.compose.material.icons.outlined.Close |
|
| 17 | + | import androidx.compose.material.icons.outlined.PersonAdd |
|
| 18 | 18 | import androidx.compose.material3.AlertDialog |
|
| 19 | 19 | import androidx.compose.material3.Button |
|
| 20 | 20 | import androidx.compose.material3.Checkbox |
|
| 21 | - | import androidx.compose.material3.ElevatedCard |
|
| 22 | 21 | import androidx.compose.material3.HorizontalDivider |
|
| 23 | 22 | import androidx.compose.material3.Icon |
|
| 24 | 23 | import androidx.compose.material3.IconButton |
|
| … | 19 unchanged lines |
||
| 44 | 43 | import io.oxygenhub.android.ui.common.AppScreen |
|
| 45 | 44 | import io.oxygenhub.android.ui.common.Load |
|
| 46 | 45 | import io.oxygenhub.android.ui.common.LoadContent |
|
| 46 | + | import io.oxygenhub.android.ui.common.NoImages |
|
| 47 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 47 | 48 | import io.oxygenhub.android.ui.common.relativeTime |
|
| 48 | 49 | import io.oxygenhub.android.ui.theme.LocalStatusColors |
|
| 49 | 50 | ||
| … | 44 unchanged lines |
||
| 94 | 95 | } |
|
| 95 | 96 | if (i.assignees.isNotEmpty()) Text("Assigned to " + i.assignees.joinToString(", "), style = MaterialTheme.typography.bodySmall) |
|
| 96 | 97 | i.milestone?.let { Text("Milestone: $it", style = MaterialTheme.typography.bodySmall) } |
|
| 97 | - | if (i.body.isNotBlank()) Markdown(i.body) else Text("No description.", color = MaterialTheme.colorScheme.onSurfaceVariant) |
|
| 98 | + | if (i.body.isNotBlank()) Markdown(i.body, imageTransformer = NoImages) else Text("No description.", color = MaterialTheme.colorScheme.onSurfaceVariant) |
|
| 98 | 99 | } |
|
| 99 | 100 | HorizontalDivider() |
|
| 100 | 101 | } |
|
| … | 21 unchanged lines |
||
| 122 | 123 | ||
| 123 | 124 | @Composable |
|
| 124 | 125 | private fun CommentCard(c: Comment) { |
|
| 125 | - | ElevatedCard(Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 6.dp)) { |
|
| 126 | + | Panel(Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 6.dp)) { |
|
| 126 | 127 | Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { |
|
| 127 | 128 | Text("${c.author} · ${relativeTime(c.createdMs)}", style = MaterialTheme.typography.labelMedium) |
|
| 128 | - | Markdown(c.body) |
|
| 129 | + | Markdown(c.body, imageTransformer = NoImages) |
|
| 129 | 130 | } |
|
| 130 | 131 | } |
|
| 131 | 132 | } |
|
| … | 112 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/projects/ProjectsScreen.kt +3 −3
| 1 | 1 | package io.oxygenhub.android.ui.projects |
|
| 2 | 2 | ||
| 3 | 3 | import androidx.compose.foundation.ExperimentalFoundationApi |
|
| 4 | + | import androidx.compose.foundation.clickable |
|
| 4 | 5 | import androidx.compose.foundation.combinedClickable |
|
| 5 | - | import androidx.compose.foundation.clickable |
|
| 6 | 6 | import androidx.compose.foundation.layout.Arrangement |
|
| 7 | 7 | import androidx.compose.foundation.layout.Box |
|
| 8 | 8 | import androidx.compose.foundation.layout.Column |
|
| … | 10 unchanged lines |
||
| 19 | 19 | import androidx.compose.material.icons.outlined.ViewKanban |
|
| 20 | 20 | import androidx.compose.material3.DropdownMenu |
|
| 21 | 21 | import androidx.compose.material3.DropdownMenuItem |
|
| 22 | - | import androidx.compose.material3.ElevatedCard |
|
| 23 | 22 | import androidx.compose.material3.HorizontalDivider |
|
| 24 | 23 | import androidx.compose.material3.ListItem |
|
| 25 | 24 | import androidx.compose.material3.MaterialTheme |
|
| … | 15 unchanged lines |
||
| 41 | 40 | import io.oxygenhub.android.ui.common.AppScreen |
|
| 42 | 41 | import io.oxygenhub.android.ui.common.EmptyState |
|
| 43 | 42 | import io.oxygenhub.android.ui.common.LoadContent |
|
| 43 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 44 | 44 | import io.oxygenhub.android.ui.common.relativeTime |
|
| 45 | 45 | import io.oxygenhub.android.ui.theme.LocalStatusColors |
|
| 46 | 46 | import kotlinx.coroutines.launch |
|
| … | 79 unchanged lines |
||
| 126 | 126 | var menu by remember { mutableStateOf(false) } |
|
| 127 | 127 | val status = LocalStatusColors.current |
|
| 128 | 128 | Box { |
|
| 129 | - | ElevatedCard( |
|
| 129 | + | Panel( |
|
| 130 | 130 | Modifier.fillMaxWidth().padding(horizontal = 12.dp).combinedClickable( |
|
| 131 | 131 | onClick = { if (card.kind != "note") onOpen() }, |
|
| 132 | 132 | onLongClick = { if (canWrite && columns.isNotEmpty()) menu = true }, |
|
| … | 33 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/proposals/ProposalScreen.kt +4 −4
| 1 | 1 | package io.oxygenhub.android.ui.proposals |
|
| 2 | 2 | ||
| 3 | - | import androidx.compose.material3.OutlinedTextField |
|
| 4 | 3 | import androidx.compose.foundation.clickable |
|
| 5 | 4 | import androidx.compose.foundation.layout.Arrangement |
|
| 6 | 5 | import androidx.compose.foundation.layout.Box |
|
| … | 20 unchanged lines |
||
| 27 | 26 | import androidx.compose.material3.AlertDialog |
|
| 28 | 27 | import androidx.compose.material3.Button |
|
| 29 | 28 | import androidx.compose.material3.CircularProgressIndicator |
|
| 30 | - | import androidx.compose.material3.ElevatedCard |
|
| 31 | 29 | import androidx.compose.material3.HorizontalDivider |
|
| 32 | 30 | import androidx.compose.material3.Icon |
|
| 33 | 31 | import androidx.compose.material3.IconButton |
|
| … | 1 unchanged lines |
||
| 35 | 33 | import androidx.compose.material3.ListItem |
|
| 36 | 34 | import androidx.compose.material3.MaterialTheme |
|
| 37 | 35 | import androidx.compose.material3.OutlinedButton |
|
| 36 | + | import androidx.compose.material3.OutlinedTextField |
|
| 38 | 37 | import androidx.compose.material3.SnackbarHostState |
|
| 39 | 38 | import androidx.compose.material3.Switch |
|
| 40 | 39 | import androidx.compose.material3.Text |
|
| … | 16 unchanged lines |
||
| 57 | 56 | import io.oxygenhub.android.api.Proposal |
|
| 58 | 57 | import io.oxygenhub.android.ui.common.AppScreen |
|
| 59 | 58 | import io.oxygenhub.android.ui.common.LoadContent |
|
| 59 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 60 | 60 | import io.oxygenhub.android.ui.common.relativeTime |
|
| 61 | 61 | import io.oxygenhub.android.ui.issues.LabelChip |
|
| 62 | 62 | import io.oxygenhub.android.ui.theme.LocalStatusColors |
|
| … | 89 unchanged lines |
||
| 152 | 152 | val status = LocalStatusColors.current |
|
| 153 | 153 | var confirming by rememberSaveable { mutableStateOf(false) } |
|
| 154 | 154 | val working = action == LandAction.Working |
|
| 155 | - | ElevatedCard(Modifier.fillMaxWidth()) { |
|
| 155 | + | Panel(Modifier.fillMaxWidth()) { |
|
| 156 | 156 | Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { |
|
| 157 | 157 | when { |
|
| 158 | 158 | p.landed != null -> Text( |
|
| … | 186 unchanged lines |
||
| 345 | 345 | private fun Section(title: String, content: @Composable () -> Unit) { |
|
| 346 | 346 | Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { |
|
| 347 | 347 | Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold) |
|
| 348 | - | ElevatedCard(Modifier.fillMaxWidth()) { content() } |
|
| 348 | + | Panel(Modifier.fillMaxWidth()) { content() } |
|
| 349 | 349 | } |
|
| 350 | 350 | } |
modified app/src/main/java/io/oxygenhub/android/ui/releases/Releases.kt +4 −3
| … | 13 unchanged lines |
||
| 14 | 14 | import androidx.compose.material.icons.outlined.Download |
|
| 15 | 15 | import androidx.compose.material.icons.outlined.NewReleases |
|
| 16 | 16 | import androidx.compose.material.icons.outlined.Search |
|
| 17 | - | import androidx.compose.material3.ElevatedCard |
|
| 18 | 17 | import androidx.compose.material3.HorizontalDivider |
|
| 19 | 18 | import androidx.compose.material3.Icon |
|
| 20 | 19 | import androidx.compose.material3.ListItem |
|
| … | 21 unchanged lines |
||
| 42 | 41 | import io.oxygenhub.android.ui.common.EmptyState |
|
| 43 | 42 | import io.oxygenhub.android.ui.common.Load |
|
| 44 | 43 | import io.oxygenhub.android.ui.common.LoadContent |
|
| 44 | + | import io.oxygenhub.android.ui.common.NoImages |
|
| 45 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 45 | 46 | import io.oxygenhub.android.ui.common.load |
|
| 46 | 47 | import io.oxygenhub.android.ui.common.relativeTime |
|
| 47 | 48 | import kotlinx.coroutines.CancellationException |
|
| … | 54 unchanged lines |
||
| 102 | 103 | } else { |
|
| 103 | 104 | LazyColumn(Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(8.dp)) { |
|
| 104 | 105 | items(releases, key = { it.tag }) { r -> |
|
| 105 | - | ElevatedCard(Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 4.dp)) { |
|
| 106 | + | Panel(Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 4.dp)) { |
|
| 106 | 107 | Column(Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { |
|
| 107 | 108 | Text(r.title.ifEmpty { r.tag }, style = MaterialTheme.typography.titleMedium) |
|
| 108 | 109 | Text( |
|
| … | 7 unchanged lines |
||
| 116 | 117 | style = MaterialTheme.typography.labelMedium, |
|
| 117 | 118 | color = MaterialTheme.colorScheme.onSurfaceVariant, |
|
| 118 | 119 | ) |
|
| 119 | - | if (r.notes.isNotBlank()) Markdown(r.notes) |
|
| 120 | + | if (r.notes.isNotBlank()) Markdown(r.notes, imageTransformer = NoImages) |
|
| 120 | 121 | r.assets.forEach { a -> |
|
| 121 | 122 | ListItem( |
|
| 122 | 123 | modifier = Modifier.clickable { if (a.url.isNotBlank()) uri.openUri(a.url) }, |
|
| … | 57 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/signin/SignInScreen.kt +3 −3
| … | 20 unchanged lines |
||
| 21 | 21 | import androidx.compose.material.icons.automirrored.outlined.OpenInNew |
|
| 22 | 22 | import androidx.compose.material.icons.outlined.Dns |
|
| 23 | 23 | import androidx.compose.material3.Button |
|
| 24 | - | import androidx.compose.material3.Card |
|
| 25 | 24 | import androidx.compose.material3.CardDefaults |
|
| 26 | 25 | import androidx.compose.material3.CircularProgressIndicator |
|
| 27 | 26 | import androidx.compose.material3.Icon |
|
| … | 23 unchanged lines |
||
| 51 | 50 | import io.oxygenhub.android.R |
|
| 52 | 51 | import io.oxygenhub.android.api.DeviceCode |
|
| 53 | 52 | import io.oxygenhub.android.ui.backdrop.Backdrop |
|
| 53 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 54 | 54 | import uniffi.rust_core.Backdrop as Scene |
|
| 55 | 55 | ||
| 56 | 56 | @Composable |
|
| … | 92 unchanged lines |
||
| 149 | 149 | val uri = LocalUriHandler.current |
|
| 150 | 150 | Text("Approve this code on the hub", style = MaterialTheme.typography.titleLarge, textAlign = TextAlign.Center) |
|
| 151 | 151 | Spacer(Modifier.height(16.dp)) |
|
| 152 | - | Card( |
|
| 153 | - | colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh), |
|
| 152 | + | Panel( |
|
| 153 | + | colors = CardDefaults.outlinedCardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh), |
|
| 154 | 154 | modifier = Modifier.fillMaxWidth(), |
|
| 155 | 155 | ) { |
|
| 156 | 156 | Text( |
|
| … | 34 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/theme/Color.kt +8 −0
| … | 11 unchanged lines |
||
| 12 | 12 | val DarkInk = Color(0xFFE6ECF7) |
|
| 13 | 13 | val DarkMute = Color(0xFF8592AD) |
|
| 14 | 14 | val DarkLine = Color(0xFF1F2B47) |
|
| 15 | + | // A phone's screen shows less than a monitor's between near-blacks (#24): |
|
| 16 | + | // panels and their edges a step above the site's --panel and --line, each |
|
| 17 | + | // container clearly above the one below. |
|
| 18 | + | val DarkPanel = Color(0xFF121B30) |
|
| 19 | + | val DarkPanelMid = Color(0xFF16203A) |
|
| 20 | + | val DarkPanelHigh = Color(0xFF1B2743) |
|
| 21 | + | val DarkPanelHighest = Color(0xFF21304F) |
|
| 22 | + | val DarkEdge = Color(0xFF2C3D63) |
|
| 15 | 23 | val DarkAccent = Color(0xFF00E0F0) |
|
| 16 | 24 | val DarkAccent2 = Color(0xFF0072FF) |
|
| 17 | 25 | val DarkBand = Color(0xFF0B3F9E) |
|
| … | 21 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/ui/theme/Theme.kt +25 −9
| … | 10 unchanged lines |
||
| 11 | 11 | import androidx.compose.runtime.Immutable |
|
| 12 | 12 | import androidx.compose.runtime.staticCompositionLocalOf |
|
| 13 | 13 | import androidx.compose.ui.graphics.Color |
|
| 14 | + | import androidx.compose.ui.graphics.lerp |
|
| 14 | 15 | import androidx.compose.ui.platform.LocalContext |
|
| 15 | 16 | ||
| 16 | 17 | private val DarkColorScheme = darkColorScheme( |
|
| … | 13 unchanged lines |
||
| 30 | 31 | surfaceVariant = DarkSurfaceSoft, |
|
| 31 | 32 | onSurfaceVariant = DarkMute, |
|
| 32 | 33 | surfaceContainerLowest = DarkBackground, |
|
| 33 | - | surfaceContainerLow = DarkSurface, |
|
| 34 | - | surfaceContainer = DarkSurface, |
|
| 35 | - | surfaceContainerHigh = DarkSurfaceSoft, |
|
| 36 | - | surfaceContainerHighest = DarkSurfaceSoft, |
|
| 34 | + | surfaceContainerLow = DarkPanel, |
|
| 35 | + | surfaceContainer = DarkPanelMid, |
|
| 36 | + | surfaceContainerHigh = DarkPanelHigh, |
|
| 37 | + | surfaceContainerHighest = DarkPanelHighest, |
|
| 37 | 38 | outline = DarkMute, |
|
| 38 | - | outlineVariant = DarkLine, |
|
| 39 | + | outlineVariant = DarkEdge, |
|
| 39 | 40 | error = DarkBad, |
|
| 40 | 41 | ) |
|
| 41 | 42 | ||
| … | 29 unchanged lines |
||
| 71 | 72 | ||
| 72 | 73 | val LocalStatusColors = staticCompositionLocalOf { StatusColors(LightOk, LightBad, LightWarn) } |
|
| 73 | 74 | ||
| 75 | + | /** |
|
| 76 | + | * Wallpaper colours in the dark, with containers lifted off the background |
|
| 77 | + | * (#24): Android's dark tones sit a shade apart, which a phone's screen hardly |
|
| 78 | + | * shows. Each container a clear step up, and edges brighter than the default. |
|
| 79 | + | */ |
|
| 80 | + | internal fun lifted(scheme: ColorScheme): ColorScheme { |
|
| 81 | + | val step = { f: Float -> lerp(scheme.background, scheme.onSurface, f) } |
|
| 82 | + | return scheme.copy( |
|
| 83 | + | surfaceContainerLow = step(0.06f), |
|
| 84 | + | surfaceContainer = step(0.09f), |
|
| 85 | + | surfaceContainerHigh = step(0.12f), |
|
| 86 | + | surfaceContainerHighest = step(0.15f), |
|
| 87 | + | outlineVariant = step(0.26f), |
|
| 88 | + | ) |
|
| 89 | + | } |
|
| 90 | + | ||
| 74 | 91 | @Composable |
|
| 75 | 92 | fun OxygenHubTheme( |
|
| 76 | 93 | darkTheme: Boolean = isSystemInDarkTheme(), |
|
| 77 | - | // Wallpaper colours (Android 12, the app's minimum, has them); the |
|
| 78 | - | // hub's own palette when turned off. |
|
| 79 | - | dynamicColor: Boolean = true, |
|
| 94 | + | // The hub's own palette, unless Account's "Wallpaper colours" is on (#24). |
|
| 95 | + | dynamicColor: Boolean = rememberWallpaperColours(), |
|
| 80 | 96 | content: @Composable () -> Unit, |
|
| 81 | 97 | ) { |
|
| 82 | 98 | val colorScheme: ColorScheme = when { |
|
| 83 | 99 | dynamicColor -> { |
|
| 84 | 100 | val context = LocalContext.current |
|
| 85 | - | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) |
|
| 101 | + | if (darkTheme) lifted(dynamicDarkColorScheme(context)) else dynamicLightColorScheme(context) |
|
| 86 | 102 | } |
|
| 87 | 103 | darkTheme -> DarkColorScheme |
|
| 88 | 104 | else -> LightColorScheme |
|
| … | 6 unchanged lines |
modified app/src/main/java/io/oxygenhub/android/update/AppUpdate.kt +2 −2
| … | 2 unchanged lines |
||
| 3 | 3 | import androidx.compose.foundation.layout.fillMaxWidth |
|
| 4 | 4 | import androidx.compose.material.icons.Icons |
|
| 5 | 5 | import androidx.compose.material.icons.outlined.SystemUpdate |
|
| 6 | - | import androidx.compose.material3.Card |
|
| 7 | 6 | import androidx.compose.material3.Icon |
|
| 8 | 7 | import androidx.compose.material3.ListItem |
|
| 9 | 8 | import androidx.compose.material3.Text |
|
| … | 7 unchanged lines |
||
| 17 | 16 | import androidx.compose.ui.platform.LocalUriHandler |
|
| 18 | 17 | import io.oxygenhub.android.api.HubApi |
|
| 19 | 18 | import io.oxygenhub.android.api.Release |
|
| 19 | + | import io.oxygenhub.android.ui.common.Panel |
|
| 20 | 20 | ||
| 21 | 21 | /** Where the app's own releases are (#7), built and signed by OxygenFlow. */ |
|
| 22 | 22 | const val APP_HUB = "https://oxygenhub.io" |
|
| … | 45 unchanged lines |
||
| 68 | 68 | } |
|
| 69 | 69 | } |
|
| 70 | 70 | val uri = LocalUriHandler.current |
|
| 71 | - | Card(Modifier.fillMaxWidth()) { |
|
| 71 | + | Panel(Modifier.fillMaxWidth()) { |
|
| 72 | 72 | ListItem( |
|
| 73 | 73 | leadingContent = { Icon(Icons.Outlined.SystemUpdate, contentDescription = null) }, |
|
| 74 | 74 | headlineContent = { Text(update?.let { "Version ${it.version} is out" } ?: "OxygenHub $installed") }, |
|
| … | 7 unchanged lines |