I've a dashboard website (Grafana) that I would like to present on my 4K TV in the office using a Chromecast with Google TV (4K). I'm trying to create a very tiny Android TV app (minimum SDK: API 31) with a embedded web browser displaying this static dashboard. The application is installed on the Chromecast by uploading the APK to my Google Drive and installing it using TV File Commander.
When I start the application on my Chromecast the web view is running in low resolution. I've changed the app to visit this website instead -- and it seems that the browser is only running in 540x960 pixels: https://www.webfx.com/tools/whats-my-browser-size/
How can I fix this?
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_browse_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:deviceIds="tv"
tools:ignore="MergeRootFrame" >
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent" android:id="@+id/webView"/>
</FrameLayout>
MainActivity
package com.company.browser
import android.os.Bundle
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.fragment.app.FragmentActivity
class MainActivity : FragmentActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById(R.id.webView)
val webSettings: WebSettings = webView.settings
webSettings.domStorageEnabled = true
webSettings.javaScriptEnabled = true
webView.loadUrl("https://www.webfx.com/tools/whats-my-browser-size/")
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.software.leanback"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Browser" >
<activity
android:name=".MainActivity"
android:banner="@drawable/app_icon_your_company"
android:exported="true"
android:icon="@drawable/app_icon_your_company"
android:label="@string/app_name"
android:logo="@drawable/app_icon_your_company"
android:screenOrientation="landscape" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
WebViews on Android TVs are quite frustrating (at least for me). Hope this helps you to understand the root cause and the approaches to address it.
The WebFx browser size tool shows you the CSS pixels. Those are logical and mapped onto physical screen pixels in a certain way to take dpi into account.
Most of Android TV dongles I've seen have window.devicePixelRatio == 4
. You can easily check it if you have access to JS console.
It means that it's 4 physical pixels per logical one.
4K is 3840x2160
4K / 4 is exactly 540x960
devicePixelRatio
for WebView is calculated the same way as density independent pixels in android UI.
1:1 density is 160, other density buckets are multiples or fractions of it.
You can check your device's density using the following adb command:
$ adb shell wm density
Physical density: 640
The value above (640) is from my Chromecast. Now following the guidance on the image above it's 640 = 160 * 4 or 4x the 1:1 density. WebView backed by chrome does this math and sets window.devicePixelRatio
to 4. That code is not accessible unless you're into patching and building chromium from scratch.
The density is the same (640) on Android 4K TV emulator, by the way. You can use it for debugging and experiments.
Where does density of 640 come from? It's set by device developers when they build the device's firmware.
These are the three basic options that come into my mind, but it's not an exhaustive list, just to push you to the right direction.
You can fully adjust CSS of your page to take screen density into account using -webkit-device-pixel-ratio
#header {
/* dimensions */
}
@media screen and (-webkit-device-pixel-ratio: 1.5) {
/* CSS for high-density screens */
#header {
/* overridden dimensions or scale value etc. */
}
}
More details here: https://developer.android.com/develop/ui/views/layout/webapps/targeting#DensityCSS
It will affect the UI of every app and increase the load on GPU, but still a valid option.
$ adb shell wm density ${YOUR_DENSITY_VALUE}
Very hacky, but works.
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
view?.loadUrl("javascript:document.body.style.zoom = 1/window.devicePixelRatio;")
super.onPageFinished(view, url)
}
}