Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.view.KeyEvent
import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.FrameLayout
Expand Down Expand Up @@ -38,6 +39,7 @@ const val BLANK_URL = "about:blank"
* - Night mode support
* - Custom user agent
* - Transparent background
* - DPAD_DOWN to TAB remapping for Android TV navigation (via `OnKeyListener`)
*
* This composable provides a convenient way to embed a WebView within a Jetpack Compose UI.
* Further customization of the WebView instance is possible through the [configure] lambda.
Expand All @@ -53,6 +55,8 @@ const val BLANK_URL = "about:blank"
* @param modifier The modifier to be applied to this WebView.
* @param nightModeTheme current [NightModeTheme]
* @param configure A lambda that allows for customization of the WebView instance.
* Note: calling `setOnKeyListener` in this lambda will override the default
* DPAD_DOWN to TAB remapping.
* @param factory A lambda that creates the WebView instance. If this returns null, a new
* WebView will be created with the current context. This is useful for providing
* a pre-configured WebView instance.
Expand Down Expand Up @@ -90,6 +94,7 @@ fun HAWebView(
FrameLayout.LayoutParams.MATCH_PARENT,
)
defaultSettings()
remapDpadDownToTab()
configure(this)
}
Comment thread
TimoPtr marked this conversation as resolved.
} catch (t: Throwable) {
Expand Down Expand Up @@ -123,6 +128,23 @@ fun HAWebView(
}
}

/**
* Remaps DPAD_DOWN key events to TAB for Android TV remote control navigation.
*
* Android TV remotes send DPAD_DOWN events which don't navigate the web frontend
* properly. Remapping to TAB allows basic element-to-element navigation in the WebView.
*/
private fun WebView.remapDpadDownToTab() {
setOnKeyListener { view, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && event.action == KeyEvent.ACTION_DOWN) {
view.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB))
true
} else {
false
}
}
}
Comment on lines +137 to +146
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces new key-handling behavior (DPAD_DOWN -> TAB) but there is no automated test covering it. Please add a unit/Robolectric or Compose UI test that verifies a DPAD_DOWN key event dispatched to the WebView results in a TAB key event being forwarded, to prevent regressions across Android TV and non-TV devices.

Copilot generated this review using guidance from repository custom instructions.

fun WebView.settings(configureDsl: WebSettings.() -> Unit) {
try {
settings.configureDsl()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import android.text.method.HideReturnsTransformationMethod
import android.text.method.PasswordTransformationMethod
import android.util.DisplayMetrics
import android.util.Rational
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
Expand Down Expand Up @@ -2163,16 +2162,6 @@ class WebViewActivity :
evaluateJavascript(eventCode, null)
}

override fun dispatchKeyEvent(event: KeyEvent): Boolean {
// Workaround to sideload on Android TV and use a remote for basic navigation in WebView
if (event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN && event.action == KeyEvent.ACTION_DOWN) {
dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB))
return true
}

return super.dispatchKeyEvent(event)
}

private fun setWebViewZoom() = lifecycleScope.launch {
// Set base zoom level (percentage must be scaled to device density/percentage)
webView.setInitialScale((resources.displayMetrics.density * presenter.getPageZoomLevel()).toInt())
Expand Down
Loading