add Android edge-to-edge display support and emulator testing instructions

This commit is contained in:
2026-10-30 20:35:33 -07:00
parent 60b24bf9e4
commit 7b6fc838b8
4 changed files with 98 additions and 25 deletions

View File

@@ -1,36 +1,73 @@
package com.vyla.go package com.vyla.go
import android.graphics.Bitmap import android.graphics.Color
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.WindowManager
import android.view.ViewGroup import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient
import android.webkit.WebView import android.webkit.WebView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
class MainActivity : TauriActivity() { class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) { private var webView: WebView? = null
super.onCreate(savedInstanceState) private var insetTop = 0f
private var insetRight = 0f
private var insetBottom = 0f
private var insetLeft = 0f
val root = findViewById<ViewGroup>(android.R.id.content) private inner class InsetBridge {
root.viewTreeObserver.addOnGlobalLayoutListener { @JavascriptInterface
findAndHookWebView(root) fun get(): String =
} "{\"top\":$insetTop,\"right\":$insetRight,\"bottom\":$insetBottom,\"left\":$insetLeft}"
} }
private fun findAndHookWebView(view: View) { override fun onCreate(savedInstanceState: Bundle?) {
if (view is WebView) { WindowCompat.setDecorFitsSystemWindows(window, false)
view.webChromeClient = object : WebChromeClient() { window.statusBarColor = Color.TRANSPARENT
override fun getDefaultVideoPoster(): Bitmap { window.navigationBarColor = Color.TRANSPARENT
return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
} window.isNavigationBarContrastEnforced = false
} window.isStatusBarContrastEnforced = false
return
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
super.onCreate(savedInstanceState)
WindowInsetsControllerCompat(window, window.decorView).apply {
isAppearanceLightStatusBars = false
isAppearanceLightNavigationBars = false
}
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { _, insets ->
val bars = insets.getInsets(
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()
)
val d = resources.displayMetrics.density
insetTop = bars.top / d
insetRight = bars.right / d
insetBottom = bars.bottom / d
insetLeft = bars.left / d
pushInsets()
insets
}
ViewCompat.requestApplyInsets(window.decorView)
}
if (view is ViewGroup) { override fun onWebViewCreate(webView: WebView) {
for (i in 0 until view.childCount) { this.webView = webView
findAndHookWebView(view.getChildAt(i)) webView.addJavascriptInterface(InsetBridge(), "VylaInsets")
} }
private fun pushInsets() {
val wv = webView ?: return
wv.post {
wv.evaluateJavascript(
"window.__vylaApplyInsets&&window.__vylaApplyInsets($insetTop,$insetRight,$insetBottom,$insetLeft)",
null
)
} }
} }
} }

View File

@@ -626,9 +626,37 @@ const ANDROID_VIDEO_FIX_SCRIPT: &str = r#"
})(); })();
"#; "#;
const EDGE_TO_EDGE_SCRIPT: &str = r#"
(function() {
var m = document.querySelector('meta[name=viewport]');
if (m && m.content.indexOf('viewport-fit') === -1) {
m.content += ', viewport-fit=cover';
}
function apply(t, r, b, l) {
var s = document.documentElement.style;
s.setProperty('--safe-top', t + 'px');
s.setProperty('--safe-right', r + 'px');
s.setProperty('--safe-bottom', b + 'px');
s.setProperty('--safe-left', l + 'px');
}
window.__vylaApplyInsets = apply;
try {
if (window.VylaInsets) {
var v = JSON.parse(window.VylaInsets.get());
apply(v.top, v.right, v.bottom, v.left);
}
} catch (e) {}
})();
"#;
#[cfg_attr(mobile, tauri::mobile_entry_point)] #[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() { pub fn run() {
tauri::Builder::default() tauri::Builder::default()
.on_page_load(|webview, payload| {
if payload.event() == tauri::webview::PageLoadEvent::Finished {
let _ = webview.eval(EDGE_TO_EDGE_SCRIPT);
}
})
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_opener::init())

View File

@@ -3,9 +3,17 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>Vyla Go</title> <title>Vyla Go</title>
<style> <style>
:root {
--safe-top: env(safe-area-inset-top, 0px);
--safe-right: env(safe-area-inset-right, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
--safe-left: env(safe-area-inset-left, 0px);
}
* { * {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
@@ -62,7 +70,7 @@
left: 0; left: 0;
right: 0; right: 0;
z-index: 10; z-index: 10;
padding: 20px 16px 30px 16px; padding: 20px calc(16px + var(--safe-right)) calc(30px + var(--safe-bottom)) calc(16px + var(--safe-left));
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -122,7 +130,7 @@
</div> </div>
<div class="center-brand"> <div class="center-brand">
<img src="https://gitlab.com/vyla-entertainment/go/-/raw/main/assets/logos/white_logo.png" alt="Logo" /> <img src="/white_logo.png" alt="Logo" />
</div> </div>
<div class="bottom-dock"> <div class="bottom-dock">

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB