0.3 intro: a recommended apps screen, and AntennaPod in the recommended list

This commit is contained in:
Seb3thehacker committed 2026-10-12 14:12:04 -07:00
1 parent 7d6b9cf578
commit 8e98851e47
2 files changed
+66 -2

No files matched your search

@@ -123,7 +123,7 @@ private fun introColors(): IntroColors {
)
}
private enum class IntroStep { WELCOME, WIRELESS, KNOBS, VOICE, GRAPHENE, THANKS, DRIVE }
private enum class IntroStep { WELCOME, WIRELESS, KNOBS, VOICE, GRAPHENE, THANKS, APPS, DRIVE }
private val Pop = CubicBezierEasing(0.3f, 1.6f, 0.5f, 1f)
private val Rise = CubicBezierEasing(0.2f, 0.8f, 0.2f, 1f)
@@ -180,6 +180,9 @@ fun IntroScreen(onDone: () -> Unit) {
IntroStep.THANKS -> BadgeStep(c, ::cq, ::cqSp, c.tertiaryC, "Thank you.",
"To everyone sending usage notes: they show me which cars Gearslip works in and which " +
"ones still fail, so I know what to fix next. Every note helps.", step) { HeartIcon(c.onTertiaryC) }
IntroStep.APPS -> BadgeStep(c, ::cq, ::cqSp, c.secondaryC, "Good company.",
"Free, open-source apps that work well in the car. Tap one to see it on GitHub.", step,
extra = { AppPicks(c, ::cq, ::cqSp, step) }) { StarIcon(c.onSecondaryC) }
IntroStep.DRIVE -> BadgeStep(c, ::cq, ::cqSp, c.primaryC, "Let's drive.",
"That's ${Intro.release}. Plug in, or just get in.", step,
action = "Start" to finish) { CheckIcon(c.onPrimaryC) }
@@ -412,7 +415,7 @@ private fun percentOf(state: SpeechModels.State) = when (state) {
}
private val SAMPLE_COMMANDS = listOf(
"Navigate to Mission Viejo", "Play Daft Punk", "What's playing?", "Next song",
"Play Daft Punk", "What's playing?", "Next song",
"Call Mom", "Open Organic Maps", "Pause", "Help",
)
@@ -461,6 +464,43 @@ private fun CommandDrift(c: IntroColors, cq: (Float) -> Dp, cqSp: (Float) -> Tex
}
}
private val APP_PICKS = listOf(
Triple("Music", "Metrolist", "https://github.com/MetrolistGroup/Metrolist"),
Triple("Maps", "Vela", "https://github.com/PimpinPumpkin/Vela"),
Triple("Podcasts", "AntennaPod", "https://github.com/AntennaPod/AntennaPod"),
)
/** One row per pick, each opening the app's GitHub page; they arrive one after another. */
@Composable
private fun AppPicks(c: IntroColors, cq: (Float) -> Dp, cqSp: (Float) -> TextUnit, key: IntroStep) {
val context = LocalContext.current
Column(Modifier.padding(top = cq(6f)), verticalArrangement = Arrangement.spacedBy(cq(2.6f))) {
APP_PICKS.forEachIndexed { i, (kind, name, url) ->
Row(
Modifier
.fillMaxWidth()
.up(entrance(key, 500 + i * 120, 700), cq(7f))
.clip(RoundedCornerShape(cq(5f)))
.background(c.secondaryC)
.clickable {
runCatching {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
}
.padding(horizontal = cq(5f), vertical = cq(3.4f)),
verticalAlignment = Alignment.CenterVertically,
) {
Column(Modifier.weight(1f)) {
Text(kind.uppercase(), color = c.onSecondaryC.copy(alpha = 0.7f),
style = TextStyle(fontSize = cqSp(3.2f), fontWeight = FontWeight.Bold, letterSpacing = cqSp(0.3f)))
Text(name, color = c.onSecondaryC, style = TextStyle(fontSize = cqSp(5.2f), fontWeight = FontWeight.ExtraBold))
}
Text("GitHub ↗", color = c.onSecondaryC, style = TextStyle(fontSize = cqSp(3.8f), fontWeight = FontWeight.SemiBold))
}
}
}
}
/** Material's scalloped "cookie": a circle whose radius waves around its edge. */
@Composable
private fun Cookie(color: Color, modifier: Modifier) {
@@ -564,6 +604,27 @@ private fun MicIcon(color: Color) {
}
}
@Composable
private fun StarIcon(color: Color) {
val turn by rememberInfiniteTransition(label = "star").animateFloat(
-8f, 8f, infiniteRepeatable(tween(1600, easing = FastOutSlowInEasing), RepeatMode.Reverse), label = "star",
)
Canvas(Modifier.fillMaxSize()) {
inIconBox {
val star = Path()
for (i in 0 until 10) {
val r = if (i % 2 == 0) 20f else 8.5f
val a = (-90 + i * 36) * PI / 180
val x = 24f + (r * cos(a)).toFloat()
val y = 25f + (r * sin(a)).toFloat()
if (i == 0) star.moveTo(x, y) else star.lineTo(x, y)
}
star.close()
rotate(turn, Offset(24f, 25f)) { drawPath(star, color) }
}
}
}
@Composable
private fun ShieldIcon(color: Color, tick: Color) {
val p = entrance(IntroStep.GRAPHENE, 800, 500)
@@ -54,6 +54,9 @@ private val recommended = listOf(
RecommendedApp("ViVi Music", "com.vivi.vivimusic", "https://github.com/vivizzz007/vivi-music", "A YouTube Music player."),
RecommendedApp("Metrolist", "com.metrolist.music", "https://github.com/MetrolistGroup/Metrolist", "A YouTube Music player."),
),
"Podcasts" to listOf(
RecommendedApp("AntennaPod", "de.danoeh.antennapod", "https://github.com/AntennaPod/AntennaPod", "Podcasts, chapters and all."),
),
)
/** Opened from Settings: the same list the setup guide ends with. */