diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/IntroScreen.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/IntroScreen.kt index 974a4c0..84b94be 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/IntroScreen.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/IntroScreen.kt @@ -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) diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/RecommendedApps.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/RecommendedApps.kt index 2a84106..4ee88bf 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/RecommendedApps.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/ui/RecommendedApps.kt @@ -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. */