Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -41,7 +41,16 @@ fun heatmapColor(level: Int, palette: HeatmapPalette = DayPalette): Color =
if (level <= 0) Color.Transparent
else MaterialTheme.colorScheme.primary.copy(alpha = levelAlpha(level, palette))

/** The "Less, five dots, More" key explaining the colors. */
/** The shape of a day in the calendars: a pool tile with softened corners. */
val PoolTileShape = RoundedCornerShape(6.dp)

/** A day without entries: a bare tile, barely there, so the month still reads as a tiled floor. */
@Composable fun emptyTileColor(): Color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.06f)

/** The lines between the tiles of a calendar, like the grout between a pool's tiles. */
@Composable fun groutColor(): Color = MaterialTheme.colorScheme.primary.copy(alpha = 0.10f)

/** The "Less, five tiles, More" key explaining the colors. */
@Composable
fun HeatmapLegend(modifier: Modifier = Modifier) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
Expand All @@ -56,9 +65,8 @@ fun HeatmapLegend(modifier: Modifier = Modifier) {
Modifier.padding(start = 4.dp)
.size(12.dp)
.background(
if (level == 0) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.08f)
else heatmapColor(level),
CircleShape,
if (level == 0) emptyTileColor() else heatmapColor(level),
RoundedCornerShape(3.dp),
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
Expand Down Expand Up @@ -53,8 +53,9 @@ object MonthCalendarTestTags {

/**
* A month calendar in the spirit of the iPhone's: a title with previous/next buttons, a row of
* weekday initials and a grid of round days that fills the width. Days with entries are filled,
* darker for more entries. Swipe sideways or use the buttons to change month.
* weekday initials and a grid of days that fills the width, laid like the tiles of a pool. Days
* with entries are filled, deeper blue for more entries. Swipe sideways or use the buttons to
* change month.
*
* @param months The months that can be shown, oldest first; it opens on the last one.
* @param countsByDay Entries per day, see [entryCountsByDay].
Expand Down Expand Up @@ -169,7 +170,14 @@ fun MonthCalendar(
remember(months[page], countsByDay, today, firstDayOfWeek, maxCount) {
buildMonthGrid(months[page], countsByDay, today, firstDayOfWeek, maxCount)
}
Column(modifier = Modifier.fillMaxWidth()) {
// The light lines between the tiles, like the grout of a pool floor.
Column(
modifier =
Modifier.fillMaxWidth()
.padding(top = 4.dp)
.background(groutColor(), RoundedCornerShape(10.dp))
.padding(3.dp),
) {
grid.weeks.forEach { week ->
Row(modifier = Modifier.fillMaxWidth()) {
(0 until 7).forEach { column ->
Expand Down Expand Up @@ -221,8 +229,12 @@ internal fun DayCell(
modifier =
Modifier.fillMaxWidth()
.aspectRatio(1f)
.background(heatmapColor(day.level), CircleShape)
.then(if (ring != null) Modifier.border(ring, CircleShape) else Modifier)
.background(
if (day.level == 0 && !day.isFuture) emptyTileColor()
else heatmapColor(day.level),
PoolTileShape,
)
.then(if (ring != null) Modifier.border(ring, PoolTileShape) else Modifier)
.then(if (day.isFuture) Modifier else Modifier.clickable(onClick = onClick))
.semantics { contentDescription = description }
.testTag(testTag),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.github.se.pooltrack.ui.history.heatmap

import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.test.junit4.createComposeRule
import com.github.se.pooltrack.ui.theme.PoolTrackTheme
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class PoolTileColorsTest {

@get:Rule val composeRule = createComposeRule()

private data class Resolved(
val scheme: ColorScheme,
val emptyTile: Color,
val grout: Color,
val lightestFill: Color,
)

private fun resolve(darkTheme: Boolean): Resolved {
lateinit var resolved: Resolved
composeRule.setContent {
PoolTrackTheme(darkTheme = darkTheme) {
resolved =
Resolved(
MaterialTheme.colorScheme,
emptyTileColor(),
groutColor(),
heatmapColor(1),
)
}
}
composeRule.waitForIdle()
return resolved
}

@Test
fun emptyTile_isAFaintVeilOfTheTextColor_inLightTheme() {
val resolved = resolve(darkTheme = false)

assertEquals(resolved.scheme.onSurface.copy(alpha = 0.06f), resolved.emptyTile)
}

@Test
fun emptyTile_isAFaintVeilOfTheTextColor_inDarkTheme() {
val resolved = resolve(darkTheme = true)

assertEquals(resolved.scheme.onSurface.copy(alpha = 0.06f), resolved.emptyTile)
}

@Test
fun emptyTile_staysFainterThanADayWithOneEntry() {
val resolved = resolve(darkTheme = true)

assertTrue(resolved.emptyTile.alpha < resolved.lightestFill.alpha)
}

@Test
fun grout_isALightTintOfThePrimaryColor() {
val resolved = resolve(darkTheme = false)

assertEquals(resolved.scheme.primary.copy(alpha = 0.10f), resolved.grout)
}
}
Loading