Skip to content

Commit 3191647

Browse files
committed
Improve JanusLoader and match colors everywhere
1 parent d8fc2ab commit 3191647

8 files changed

Lines changed: 79 additions & 32 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353

5454
# Documentation
5555
- **Design & Branding**: [docs/DESIGN.md](docs/DESIGN.md) - UI/UX strategy, branding, typography, and themes.
56-
- **Initial Design**: [docs/INITIAL-DESIGN.md](docs/INITIAL-DESIGN.md) - Technical design document and architecture.
56+
- **Initial Design**: [docs/INITIAL-DESIGN.md](docs/INITIAL-DESIGN.md) - Original design manifest.
5757
- **Translation Flow**: [docs/TRANSLATION_FLOW.md](docs/TRANSLATION_FLOW.md) - Technical flow of translation features.
5858
- **F-Droid Submission**: [docs/FDROID_SUBMISSION.md](docs/FDROID_SUBMISSION.md) - Guide for submitting to F-Droid.

app/src/main/java/com/anysoftkeyboard/janus/app/ui/components/JanusLoader.kt

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,23 @@ import androidx.compose.foundation.Canvas
1111
import androidx.compose.foundation.Image
1212
import androidx.compose.foundation.layout.Box
1313
import androidx.compose.foundation.layout.fillMaxSize
14+
import androidx.compose.material3.MaterialTheme
1415
import androidx.compose.runtime.Composable
1516
import androidx.compose.runtime.getValue
1617
import androidx.compose.ui.Modifier
1718
import androidx.compose.ui.geometry.Offset
1819
import androidx.compose.ui.graphics.Color
1920
import androidx.compose.ui.layout.ContentScale
20-
import androidx.compose.ui.platform.LocalDensity
2121
import androidx.compose.ui.res.painterResource
22-
import androidx.compose.ui.unit.dp
2322
import androidx.core.graphics.PathParser
2423
import com.anysoftkeyboard.janus.app.R
2524

2625
// The path data from docs/loader_path.svg
2726
private const val LOADER_PATH_DATA =
2827
"M 218.00,126.00 C 218.00,126.00 231.33,130.00 231.33,130.00 231.33,130.00 243.33,138.67 243.33,138.67 243.33,138.67 251.33,148.00 251.33,148.00 251.33,148.00 254.67,161.33 254.67,161.33 254.67,161.33 253.33,178.67 253.33,178.67 253.33,178.67 252.00,192.00 252.00,192.00 252.00,192.00 247.33,206.00 247.33,206.00 247.33,206.00 243.33,217.33 243.33,217.33 243.33,217.33 239.33,226.67 239.33,226.67 239.33,226.67 234.67,235.33 234.67,235.33 234.67,235.33 225.33,246.67 225.33,246.67 225.33,246.67 204.00,274.00 204.00,274.00 204.00,274.00 197.33,280.00 197.33,280.00 197.33,280.00 187.33,286.00 187.33,286.00 187.33,286.00 174.67,290.67 174.67,290.67 174.67,290.67 160.67,291.33 160.67,291.33 160.67,291.33 148.00,288.67 148.00,288.67 148.00,288.67 137.33,280.00 137.33,280.00 137.33,280.00 128.67,269.33 128.67,269.33 128.67,269.33 124.00,255.33 124.00,255.33 124.00,255.33 124.00,240.67 124.00,240.67 124.00,240.67 129.33,230.67 129.33,230.67 129.33,230.67 140.00,223.33 140.00,223.33 140.00,223.33 151.33,216.67 151.33,216.67 151.33,216.67 164.00,211.33 164.00,211.33 164.00,211.33 176.67,206.00 176.67,206.00 176.67,206.00 190.00,203.33 190.00,203.33 190.00,203.33 203.33,200.67 203.33,200.67 203.33,200.67 216.00,199.33 216.00,199.33 216.00,199.33 231.33,200.00 231.33,200.00 231.33,200.00 265.33,207.33 265.33,207.33 265.33,207.33 276.67,212.00 276.67,212.00 276.67,212.00 286.67,218.00 286.67,218.00 286.67,218.00 296.00,226.67 296.00,226.67 296.00,226.67 302.67,235.33 302.67,235.33 302.67,235.33 306.67,246.67 306.67,246.67 306.67,246.67 305.33,262.00 305.33,262.00 305.33,262.00 300.00,272.00 300.00,272.00 300.00,272.00 291.33,284.67 291.33,284.67 291.33,284.67 280.67,288.00 280.67,288.00 280.67,288.00 266.00,290.00 266.00,290.00 266.00,290.00 252.67,290.00 252.67,290.00 252.67,290.00 238.67,284.00 238.67,284.00 238.67,284.00 230.00,274.67 230.00,274.67 230.00,274.67 223.33,267.33 223.33,267.33 223.33,267.33 211.33,255.33 211.33,255.33 211.33,255.33 204.67,246.67 204.67,246.67 204.67,246.67 198.00,235.33 198.00,235.33 198.00,235.33 190.00,220.00 190.00,220.00 190.00,220.00 178.00,186.00 178.00,186.00 178.00,186.00 177.33,172.67 177.33,172.67 177.33,172.67 178.00,158.67 178.00,158.67 178.00,158.67 180.67,146.00 180.67,146.00 180.67,146.00 188.67,138.00 188.67,138.00 188.67,138.00 202.00,130.00 202.00,130.00"
2928

30-
// The Gold Color requested
31-
private val GoldColor = Color(0xFFFFD700)
29+
private const val viewPortSize = 432f
30+
private const val dotRadiusFactor = 0.03f
3231

3332
private val NormalizedPathPoints: List<Offset> by lazy {
3433
val path = PathParser.createPathFromPathData(LOADER_PATH_DATA)
@@ -41,15 +40,21 @@ private val NormalizedPathPoints: List<Offset> by lazy {
4140
for (i in 0 until pointsCount) {
4241
val distance = (i.toFloat() / pointsCount) * length
4342
pathMeasure.getPosTan(distance, pos, tan)
44-
// Normalize by viewport size 432x432
45-
points.add(Offset(pos[0] / 432f, pos[1] / 432f))
43+
// Normalize by viewport size
44+
points.add(Offset(pos[0] / viewPortSize, pos[1] / viewPortSize))
4645
}
4746
points
4847
}
4948

5049
@Composable
51-
fun JanusLoader(modifier: Modifier = Modifier, durationMillis: Int = 1000) {
52-
// 1. Define the Animation
50+
fun JanusLoader(
51+
modifier: Modifier = Modifier,
52+
durationMillis: Int = 2000,
53+
tint: Color = MaterialTheme.colorScheme.primary,
54+
dotColor: Color = MaterialTheme.colorScheme.onPrimary,
55+
dotColorFill: Color = MaterialTheme.colorScheme.secondary
56+
) {
57+
// Define the Animation
5358
val infiniteTransition = rememberInfiniteTransition(label = "JanusLoaderAnimation")
5459
val progress by
5560
infiniteTransition.animateFloat(
@@ -61,29 +66,56 @@ fun JanusLoader(modifier: Modifier = Modifier, durationMillis: Int = 1000) {
6166
repeatMode = RepeatMode.Restart),
6267
label = "Progress")
6368

64-
// 2. Density for converting dp to px
65-
val density = LocalDensity.current
66-
val dotRadiusPx = with(density) { 4.dp.toPx() }
67-
6869
Box(modifier = modifier) {
6970
// Background Image
7071
Image(
7172
painter = painterResource(id = R.mipmap.ic_launcher_foreground),
7273
contentDescription = null,
7374
modifier = Modifier.fillMaxSize(),
74-
contentScale = ContentScale.Fit)
75-
76-
// 3. Draw Content
75+
contentScale = ContentScale.Fit,
76+
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(tint))
77+
// Traveling dots
7778
Canvas(modifier = Modifier.fillMaxSize()) {
7879
val points = NormalizedPathPoints
79-
val index = (progress * (points.size - 1)).toInt().coerceIn(0, points.size - 1)
80-
val normalizedPoint = points[index]
80+
fun drawTravelingDot(index: Int, dotRadiusPx: Float) {
81+
val normalizedPoint = points[index]
82+
val dotPosition =
83+
Offset(x = normalizedPoint.x * size.width, y = normalizedPoint.y * size.height)
84+
drawCircle(color = dotColor, radius = dotRadiusPx, center = dotPosition)
85+
drawCircle(color = dotColorFill, radius = dotRadiusPx - 2, center = dotPosition)
86+
}
8187

82-
val dotPosition =
83-
Offset(x = normalizedPoint.x * size.width, y = normalizedPoint.y * size.height)
88+
val baseDotRadiusPx = size.width * dotRadiusFactor
89+
val pointsCount = points.size
8490

85-
// Draw the Traveling Dot
86-
drawCircle(color = GoldColor, radius = dotRadiusPx, center = dotPosition)
91+
val dots =
92+
List(4) { level ->
93+
TravelingDot(
94+
pointsCount = pointsCount,
95+
durationMillis = durationMillis,
96+
baseDotRadiusPx = baseDotRadiusPx,
97+
delayLevel = level,
98+
drawTravelingDot = ::drawTravelingDot)
99+
}
100+
101+
dots.forEach { it.draw(progress) }
87102
}
88103
}
89104
}
105+
106+
private class TravelingDot(
107+
private val pointsCount: Int,
108+
private val durationMillis: Int,
109+
private val baseDotRadiusPx: Float,
110+
private val delayLevel: Int,
111+
private val drawTravelingDot: (Int, Float) -> Unit
112+
) {
113+
private val delay = delayLevel * 70f
114+
private val radius = baseDotRadiusPx / (1 + delayLevel)
115+
116+
fun draw(progress: Float) {
117+
val delayedProgress = (progress - (delay / durationMillis)).let { if (it < 0) it + 1f else it }
118+
val index = (delayedProgress * (pointsCount - 1)).toInt().coerceIn(0, pointsCount - 1)
119+
drawTravelingDot(index, radius)
120+
}
121+
}

app/src/main/java/com/anysoftkeyboard/janus/app/ui/items/SearchResultItem.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,6 @@ fun SearchResultItem(
7979

8080
// Content area: loading, error, available languages, or snippet
8181
when {
82-
isLoading -> {
83-
JanusLoader(modifier = Modifier.size(24.dp))
84-
}
8582
errorMessage != null -> {
8683
ErrorContent(errorMessage)
8784
}
@@ -97,6 +94,17 @@ fun SearchResultItem(
9794
}
9895
}
9996
}
97+
98+
if (isLoading) {
99+
Spacer(modifier = Modifier.height(4.dp))
100+
Row(verticalAlignment = Alignment.CenterVertically) {
101+
JanusLoader(modifier = Modifier.size(48.dp))
102+
Text(
103+
text = stringResource(R.string.loading_translation_for_item, result.title),
104+
style = MaterialTheme.typography.bodySmall,
105+
color = MaterialTheme.colorScheme.onSurfaceVariant)
106+
}
107+
}
100108
}
101109

102110
/** Displays error state within the search result item. */

app/src/main/java/com/anysoftkeyboard/janus/app/ui/states/EmptyState.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import com.anysoftkeyboard.janus.app.viewmodels.TranslateViewState
4040
private fun EmptyStateMessage(
4141
title: String,
4242
message: String,
43-
iconTint: Color = MaterialTheme.colorScheme.onSurfaceVariant,
43+
iconTint: Color = MaterialTheme.colorScheme.primary,
4444
iconContent: @Composable () -> Unit
4545
) {
4646
Column(
@@ -55,7 +55,7 @@ private fun EmptyStateMessage(
5555
Text(
5656
text = message,
5757
style = MaterialTheme.typography.bodyMedium,
58-
color = MaterialTheme.colorScheme.onSurfaceVariant)
58+
color = MaterialTheme.colorScheme.secondary)
5959
}
6060
}
6161
}
@@ -64,7 +64,7 @@ private fun EmptyStateMessage(
6464
private fun EmptyStateMessageWithPainter(
6565
title: String,
6666
message: String,
67-
iconTint: Color = MaterialTheme.colorScheme.onSurfaceVariant,
67+
iconTint: Color = MaterialTheme.colorScheme.primary,
6868
painter: Painter
6969
) {
7070
EmptyStateMessage(title, message, iconTint) {

app/src/main/java/com/anysoftkeyboard/janus/app/ui/states/SearchResultsView.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.runtime.remember
2323
import androidx.compose.runtime.setValue
2424
import androidx.compose.ui.Alignment
2525
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.res.painterResource
2627
import androidx.compose.ui.res.stringResource
2728
import androidx.compose.ui.unit.dp
2829
import com.anysoftkeyboard.janus.app.R
@@ -81,11 +82,11 @@ fun SearchResultsView(
8182

8283
LazyColumn {
8384
item {
84-
androidx.compose.foundation.layout.Row(
85+
Row(
8586
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
8687
verticalAlignment = Alignment.CenterVertically) {
8788
Icon(
88-
painter = androidx.compose.ui.res.painterResource(R.mipmap.ic_launcher_foreground),
89+
painter = painterResource(R.mipmap.ic_launcher_foreground),
8990
contentDescription = null,
9091
modifier = Modifier.size(32.dp),
9192
tint = MaterialTheme.colorScheme.primary)

app/src/main/java/com/anysoftkeyboard/janus/app/ui/theme/Color.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ val Charcoal = Color(0xFF242426)
88
val IlluminatedGold = Color(0xFFFFD54F)
99
val DarkTextOnGold = Color(0xFF1C1C1E)
1010
val OffWhiteText = Color(0xFFE6E1E5)
11+
val Patina = Color(0xFF85D3CB) // Pale Aqua-Grey (Glowing Oxidation)
12+
val DarkTextOnPatina = Color(0xFF003734)
1113

1214
// Light Theme Colors
1315
val WarmAlabaster = Color(0xFFF9F6F2)
@@ -16,3 +18,4 @@ val AntiqueBronze = Color(0xFF785A00)
1618
val WhiteTextOnBronze = Color(0xFFFFFFFF)
1719
val DeepInk = Color(0xFF1C1C1E)
1820
val BeigeTan = Color(0xFFE8E0D5)
21+
val Verdigris = Color(0xFF2E6B64) // Deep Muted Teal (Oxidized Bronze)

app/src/main/java/com/anysoftkeyboard/janus/app/ui/theme/JanusTheme.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ private val DarkColorScheme =
2222
background = DeepSlate,
2323
surface = Charcoal,
2424
onSurface = OffWhiteText,
25-
)
25+
secondary = Patina,
26+
onSecondary = DarkTextOnPatina)
2627

2728
private val LightColorScheme =
2829
lightColorScheme(
@@ -32,7 +33,8 @@ private val LightColorScheme =
3233
surface = PureWhite,
3334
onSurface = DeepInk,
3435
secondaryContainer = BeigeTan,
35-
)
36+
secondary = Verdigris,
37+
onSecondary = PureWhite)
3638

3739
@Composable
3840
fun JanusTheme(

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
<string name="error_unknown">Unknown error occurred</string>
7070
<string name="no_translations_available">No translations available</string>
7171
<string name="available_in_languages">Available in: %1$s</string>
72+
<string name="loading_translation_for_item">Fetching translation for %1$s...</string>
7273

7374
<!-- Repository errors -->
7475
<string name="error_page_not_found">Page not found</string>

0 commit comments

Comments
 (0)