Files
Gearslip/gearslip/src/main/java/app/seb3thehacker/gearslip/ProjectedContent.kt
T
Seb3thehacker a8b49a3672 Release v0.0.03-pre: weather, template cards, media fallbacks, setup steps
- Weather from Open-Meteo with a place name, a forecast screen and a nav bar icon; a fresh
  location fix is requested when the last one is stale
- Map controls are solid, one size, and fade out after four seconds; pinch to zoom works
  because every finger now reaches the app
- Map templates draw their content in a floating card; toggle and radio rows show their state,
  the Back and Pan actions work, and details text and buttons are larger
- Home player gains Lyrics and Minimise buttons; minimised, it lives in the nav bar
- Media apps that hide their library show what they are and can still be controlled; apps that
  refuse Gearslip as a host say why
- Debug mode switch, session report in the log, and log sharing as a file attachment
- Launcher, phone apps, weather and calendar load at startup
- Web app opens DuckDuckGo by default
- Home screen lists the setup steps; the launcher marks apps tested and working
2026-09-21 01:14:49 -07:00

68 lines
2.9 KiB
Kotlin

package app.seb3thehacker.gearslip
import android.content.Context
import java.io.File
/** Decides what the car screen shows. */
object ProjectedContent {
/**
* Precedence: the URL saved in Settings, then a `url.txt` in the external files dir (the
* pre-Settings way, still honoured so existing setups keep working), then [DEFAULT_URL].
*/
fun resolve(context: Context): String {
val saved = AppSettings.startupUrl(context)
if (saved.isNotEmpty()) {
val url = normalize(saved)
GearslipLog.i("projecting startup URL from settings: $url")
return url
}
val legacy = context.getExternalFilesDir(null)?.let { File(it, "url.txt") }
if (legacy != null && legacy.isFile) {
val url = legacy.readText().trim()
if (url.isNotEmpty()) {
GearslipLog.i("projecting url.txt override: $url")
return url
}
}
return DEFAULT_URL
}
/** What the Web app opens until a page is chosen in Settings. */
const val DEFAULT_URL = "https://duckduckgo.com"
/** A bare "example.com" is a URL the user meant; raw HTML is passed through untouched. */
private fun normalize(value: String): String =
if (value.startsWith("<") || "://" in value) value else "https://$value"
/** Big targets, a live clock, and tap feedback - all verifiable at a glance in a car. */
val TEST_PAGE = """
<html><head><meta name="viewport" content="width=device-width,initial-scale=1">
<style>
html{height:100%}
body{margin:0;font-family:sans-serif;background:#101418;color:#fff;
display:flex;flex-direction:column;height:100%}
h1{margin:12px;font-size:28px}
#t{margin:0 12px 8px;font-size:20px;color:#8ab4f8}
.row{display:flex;flex:1;gap:8px;padding:8px}
.b{flex:1;display:flex;align-items:center;justify-content:center;
font-size:26px;border-radius:12px;background:#243043}
.hit{background:#2e7d32}
</style></head><body>
<h1>Gearslip &mdash; projected from the phone</h1>
<div id="t">starting&hellip;</div>
<div class="row">
<div class="b" id="a">TAP A</div><div class="b" id="b">TAP B</div>
<div class="b" id="c">TAP C</div>
</div>
<script>
setInterval(function(){document.getElementById('t').textContent=new Date().toLocaleTimeString();},1000);
['a','b','c'].forEach(function(id){
var e=document.getElementById(id);
e.addEventListener('touchstart',function(){e.classList.add('hit');e.textContent='HIT '+id.toUpperCase();});
e.addEventListener('touchend',function(){setTimeout(function(){e.classList.remove('hit');e.textContent='TAP '+id.toUpperCase();},400);});
});
</script></body></html>
""".trimIndent()
}