kotlinwebviewandroid-windowmanager

How do you display dark mode webpages in a floating webview in Android


I have an android app where I want to display a webpage in dark mode, overlaid on top of other apps (using android draw over apps facility). I'm using a webview and windowmanager to create the floating view.

I want to target Android 9 / API 28, so that I can use the app with Amazon Fire TV.

The webpage in question is from Home Assistant. Dark mode can be set to auto, dark or light in home assistant. For the purposes of this post, i've set dark mode in the HA settings. When the HA page loads, it shows a "loading data" splash screen before showing the final screen. The issue I am experiencing is that the splash screen is shown light, whereas the final HA screen correctly displays in dark mode.

It has been suggested by the HA team that the splash screen is displayed as light or dark depending upon the browser setting. So, it seems that dark mode needs to be set on the webview.

However, in my test code, I have implemented a test where a webview is used but not floating, it's full screen. In this test, the splash screen is correctly dark. Here are the tests:

Working, but not floating

widget_web.xml

<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/web_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fadingEdge="vertical"
    android:padding="8dp"
    android:visibility="visible" />

MainActivity.kt

    setContentView(R.layout.widget_web)
    myWebView = findViewById<View>(R.id.web_view) as WebView
    myWebView.settings.javaScriptEnabled = true
    myWebView.setInitialScale(100)
    myWebView.addJavascriptInterface(WebViewJavascriptInterface(applicationContext), "externalApp")
    myWebView.loadUrl("http://192.168.1.73:8123/dashboard-studyonly?external_auth=1")

Splash screen (black, as I need it)

enter image description here

Black Home Assistant page:

enter image description here

Not working, but floating

MainActivity.kt

    lateinit var windowManager: WindowManager
    lateinit var floatView: ViewGroup
    lateinit var mParentView: ViewGroup
    lateinit var floatWindowLayoutParam: WindowManager.LayoutParams

    setContentView(R.layout.widget_web)
    floatWindowLayoutParam = WindowManager.LayoutParams(
        1000,1000,
        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT
    )

    windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
    val inflater: LayoutInflater =
        baseContext.getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater

    mParentView = FrameLayout(this)
    windowManager.addView(mParentView, floatWindowLayoutParam) //add mParentView to window with assigned parameters
    floatView = inflater.inflate(R.layout.widget_web, null) as ViewGroup //floatView holds the webView
    floatView.visibility = View.GONE
    mParentView.addView(floatView) // add floatView (webView) as child to mParentView
    myWebView = floatView.findViewById(R.id.web_view) as WebView
    myWebView.settings.javaScriptEnabled = true
    myWebView.settings.domStorageEnabled = true
    myWebView.setInitialScale(100)
    myWebView.addJavascriptInterface(WebViewJavascriptInterface(applicationContext), "externalApp")
    myWebView.loadUrl("http://192.168.1.73:8123/dashboard-studyonly?external_auth=1")
    myWebView.requestFocus()
    myWebView.visibility = View.VISIBLE
    finish() // hide the background of the app

Splash screen, white not black:

enter image description here

Black HA theme, as required (but driven by HA settings, not the webview):

enter image description here


Solution

  • Try this

        floatWindowLayoutParam = WindowManager.LayoutParams(
                1000, 1000,
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
            )
    
            windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
            val inflater: LayoutInflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater
    
            mParentView = FrameLayout(this)
            windowManager.addView(mParentView, floatWindowLayoutParam)
            
            floatView = inflater.inflate(R.layout.widget_web, mParentView, false) as ViewGroup
            floatView.visibility = View.GONE
            mParentView.addView(floatView) 
            
            myWebView = floatView.findViewById(R.id.web_view) as WebView
            myWebView.settings.javaScriptEnabled = true
            myWebView.settings.domStorageEnabled = true
            myWebView.setInitialScale(100)
            myWebView.addJavascriptInterface(WebViewJavascriptInterface(applicationContext), "externalApp")
            
            myWebView.webViewClient = object : WebViewClient() {
                override fun onPageFinished(view: WebView?, url: String?) {
                    super.onPageFinished(view, url)
                    injectDarkModeCSS(view)
                }
            }
    
            myWebView.loadUrl("http://192.168.1.73:8123/dashboard-studyonly?external_auth=1")
            myWebView.requestFocus()
            myWebView.visibility = View.VISIBLE
            finish() 
        }
    
        private fun injectDarkModeCSS(webView: WebView?) {
            val css = "(function() {" +
                      "document.body.style.backgroundColor = 'black';" +
                      "document.body.style.color = 'white';" +
                      "})()"
            webView?.evaluateJavascript("javascript:$css", null)
        }
    }