mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-11 08:12:26 +01:00
browser: page title, forward, reload, start page, and an address bar
This commit is contained in:
1 parent
b57bfb5324
commit
1481bd9c52
1 file changed
+62
-8
@@ -31,6 +31,10 @@ import androidx.compose.ui.graphics.toArgb
|
|||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import app.seb3thehacker.gearslip.GearslipLog
|
import app.seb3thehacker.gearslip.GearslipLog
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
|
import androidx.compose.material.icons.filled.Home
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A browser inside the car UI. [content] is a URL, or raw HTML when it does not look like one.
|
* A browser inside the car UI. [content] is a URL, or raw HTML when it does not look like one.
|
||||||
@@ -56,6 +60,11 @@ fun WebApp(content: String) {
|
|||||||
// the video's then leaves full screen too, instead of stranding them in it.
|
// the video's then leaves full screen too, instead of stranding them in it.
|
||||||
var videoMadeFullscreen by remember { mutableStateOf(false) }
|
var videoMadeFullscreen by remember { mutableStateOf(false) }
|
||||||
val fullscreen = CarFullscreen.active
|
val fullscreen = CarFullscreen.active
|
||||||
|
// What the header shows and what its buttons can do, refreshed as pages load.
|
||||||
|
var pageTitle by remember { mutableStateOf("") }
|
||||||
|
var canGoForward by remember { mutableStateOf(false) }
|
||||||
|
// The address being typed, or null while the header shows the page title.
|
||||||
|
var address by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
// Leaving the browser ends full screen, whoever started it.
|
// Leaving the browser ends full screen, whoever started it.
|
||||||
DisposableEffect(Unit) { onDispose { CarFullscreen.exit() } }
|
DisposableEffect(Unit) { onDispose { CarFullscreen.exit() } }
|
||||||
@@ -86,7 +95,18 @@ fun WebApp(content: String) {
|
|||||||
Column(Modifier.fillMaxSize()) {
|
Column(Modifier.fillMaxSize()) {
|
||||||
// Back steps through the page's history first (the interceptor above), then leaves.
|
// Back steps through the page's history first (the interceptor above), then leaves.
|
||||||
if (!fullscreen) {
|
if (!fullscreen) {
|
||||||
ScreenHeader("Browser", Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
|
ScreenHeader(pageTitle.ifEmpty { "Browser" }, Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
|
||||||
|
GsIconButton(androidx.compose.material.icons.Icons.Filled.Search, "Search or type an address", {
|
||||||
|
fieldText = null
|
||||||
|
address = ""
|
||||||
|
})
|
||||||
|
if (canGoForward) {
|
||||||
|
GsIconButton(androidx.compose.material.icons.Icons.AutoMirrored.Filled.ArrowForward, "Forward", { web?.goForward() })
|
||||||
|
}
|
||||||
|
GsIconButton(androidx.compose.material.icons.Icons.Filled.Refresh, "Reload", { web?.reload() })
|
||||||
|
GsIconButton(androidx.compose.material.icons.Icons.Filled.Home, "Start page", {
|
||||||
|
web?.let { view -> loadContent(view, content) }
|
||||||
|
})
|
||||||
GsIconButton(MediaIcons.Fullscreen, "Full screen", CarFullscreen::enter)
|
GsIconButton(MediaIcons.Fullscreen, "Full screen", CarFullscreen::enter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,8 +119,15 @@ fun WebApp(content: String) {
|
|||||||
settings.domStorageEnabled = true
|
settings.domStorageEnabled = true
|
||||||
settings.loadWithOverviewMode = true
|
settings.loadWithOverviewMode = true
|
||||||
settings.useWideViewPort = true
|
settings.useWideViewPort = true
|
||||||
webViewClient = LoggingClient(ring)
|
webViewClient = LoggingClient(ring) { view ->
|
||||||
|
canGoForward = view.canGoForward()
|
||||||
|
pageTitle = view.title.orEmpty().takeUnless { it.startsWith("http") || it == "about:blank" }.orEmpty()
|
||||||
|
}
|
||||||
webChromeClient = object : WebChromeClient() {
|
webChromeClient = object : WebChromeClient() {
|
||||||
|
override fun onReceivedTitle(view: WebView?, title: String?) {
|
||||||
|
pageTitle = title.orEmpty().takeUnless { it.startsWith("http") || it == "about:blank" }.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onShowCustomView(view: View, callback: CustomViewCallback) {
|
override fun onShowCustomView(view: View, callback: CustomViewCallback) {
|
||||||
videoMadeFullscreen = !CarFullscreen.active
|
videoMadeFullscreen = !CarFullscreen.active
|
||||||
pageFullscreen = view to callback
|
pageFullscreen = view to callback
|
||||||
@@ -117,11 +144,7 @@ fun WebApp(content: String) {
|
|||||||
FieldFocusBridge(onFocus = { fieldText = it }, onBlur = { fieldText = null }),
|
FieldFocusBridge(onFocus = { fieldText = it }, onBlur = { fieldText = null }),
|
||||||
"GearslipField",
|
"GearslipField",
|
||||||
)
|
)
|
||||||
if (content.startsWith("http://") || content.startsWith("https://")) {
|
loadContent(this, content)
|
||||||
loadUrl(content)
|
|
||||||
} else {
|
|
||||||
loadDataWithBaseURL(null, content, "text/html", "utf-8", null)
|
|
||||||
}
|
|
||||||
web = this
|
web = this
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -145,6 +168,17 @@ fun WebApp(content: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
address?.let { typed ->
|
||||||
|
CarKeyboard(
|
||||||
|
text = typed,
|
||||||
|
onTextChange = { address = it },
|
||||||
|
onSubmit = {
|
||||||
|
web?.loadUrl(addressToUrl(typed))
|
||||||
|
address = null
|
||||||
|
},
|
||||||
|
onDismiss = { address = null },
|
||||||
|
)
|
||||||
|
}
|
||||||
fieldText?.let { text ->
|
fieldText?.let { text ->
|
||||||
CarKeyboard(
|
CarKeyboard(
|
||||||
text = text,
|
text = text,
|
||||||
@@ -162,9 +196,29 @@ fun WebApp(content: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LoggingClient(private val ring: String) : WebViewClient() {
|
/** The start page: a web address, or a page of HTML Gearslip made itself. */
|
||||||
|
private fun loadContent(view: WebView, content: String) {
|
||||||
|
if (content.startsWith("http://") || content.startsWith("https://")) {
|
||||||
|
view.loadUrl(content)
|
||||||
|
} else {
|
||||||
|
view.loadDataWithBaseURL(null, content, "text/html", "utf-8", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Something that looks like an address opens as one; anything else is searched for. */
|
||||||
|
private fun addressToUrl(typed: String): String {
|
||||||
|
val text = typed.trim()
|
||||||
|
return when {
|
||||||
|
text.startsWith("http://") || text.startsWith("https://") -> text
|
||||||
|
' ' !in text && '.' in text -> "https://$text"
|
||||||
|
else -> "https://duckduckgo.com/?q=" + java.net.URLEncoder.encode(text, "UTF-8")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LoggingClient(private val ring: String, private val onLoaded: (WebView) -> Unit) : WebViewClient() {
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
override fun onPageFinished(view: WebView?, url: String?) {
|
||||||
GearslipLog.i("car web page loaded: $url")
|
GearslipLog.i("car web page loaded: $url")
|
||||||
|
view?.let(onLoaded)
|
||||||
view?.evaluateJavascript(FOCUS_TRACKING_JS, null)
|
view?.evaluateJavascript(FOCUS_TRACKING_JS, null)
|
||||||
view?.evaluateJavascript(focusRingJs(ring), null)
|
view?.evaluateJavascript(focusRingJs(ring), null)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user