javascriptandroidandroid-webview

How to set flag_keep_screen_on from javascript


I'd like to enable or disable the screen_on feature from JAvascript inside the webview.

This is how I tried to do it

@JavascriptInterface
fun setScreenOn(screenOn: Boolean) {
    val window = m_Activity.window
    if (screenOn) {
        Log.d("screen", "addFlag")
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
    } else {
        Log.d("screen", "clearFlag")
        window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
    }
}

Unfortunately this gives me a Java exception.

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. Expected: main Calling: JavaBridge
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:11023)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:2473)
at android.view.View.requestLayout(View.java:28020)
at android.view.View.setLayoutParams(View.java:20530)
at android.view.WindowManagerGlobal.updateViewLayout(WindowManagerGlobal.java:464)
at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:165)
at android.app.Activity.onWindowAttributesChanged(Activity.java:4364)
at android.view.Window.dispatchWindowAttributesChanged(Window.java:1323)
at com.android.internal.policy.PhoneWindow.dispatchWindowAttributesChanged(PhoneWindow.java:3193)
at android.view.Window.setFlags(Window.java:1309)
at android.view.Window.clearFlags(Window.java:1283)

Only the original thread that created a view hierarchy can touch its views. Does this mean that it is not possible to change FLAG_KEEP_SCREEN_ON from Javascript? Or is there a way to work around this?


Solution

  • This is a common issue. Android restricts you from Accessing the UI Thread from other Threads in order to prevent performance issues and an ANR (Application Not Responding) error.

    You could try to call a Java Method using the JavaScript Interface and then running the code in Java using runOnUiThread() in order to Update the Flag

    @JavascriptInterface
    public void foo(Boolean screenOn) {
        runOnUiThread(() -> {
            ... code ...
        });
    }
    

    Edit: it was not trivial for me where and how to add runOnUiThread. This is how I got it working:

    class MainActivity : ComponentActivity() {
        fun setScreenOnProxy(screenOn: Boolean) {
            runOnUiThread {
                ...
            }
        }
    }
    class JsObject internal constructor(private var activity: MainActivity)
    {
        @JavascriptInterface
        fun setScreenOn(screenOn: Boolean) {
            activity.setScreenOnProxy(screenOn)
        }
    }