Android: fix OS back gesture for SPA modals/screens (drive JS history.back)

WryActivity's generated back handler calls WebView.goBack(), which does NOT
dispatch popstate for same-document history.pushState() entries — so the OS back
gesture never reached the app's popstate handler and couldn't dismiss modals
(new product, add-to-eaten, ЧЗ scanner) or navigate screens; only the on-screen
back arrow worked. WryActivity.kt is auto-regenerated each build, so override in
the app-owned MainActivity instead: set handleBackNavigation=false and register
our own OnBackPressedCallback that runs window.history.back() via evaluateJavascript,
which fires popstate. Verified on-device: swipe-back now closes modals and the ЧЗ
scanner. (adb `input keyevent BACK` does NOT go through OnBackPressedDispatcher on
Android 13+, so it can't be used to test this — only the real gesture does.)
This commit is contained in:
4aiman 2026-08-27 23:12:34 +03:00
commit 5d042870e2

View file

@ -11,6 +11,8 @@ import android.provider.Settings
import android.os.Bundle
import android.os.FileObserver
import android.util.Log
import android.webkit.WebView
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
@ -22,6 +24,33 @@ import javax.net.ssl.HttpsURLConnection
class MainActivity : TauriActivity() {
private var commandObserver: FileObserver? = null
private var pendingSaveContent: String? = null
private var backWebView: WebView? = null
// Disable WryActivity's generated back handler: its mWebView.goBack() does NOT
// dispatch a popstate for same-document history.pushState() entries (our SPA
// nav/modals), so the OS back gesture never reached the JS handler — modals
// and screens couldn't be dismissed with a swipe. We register our own callback
// (below) that drives window.history.back() via JS, which fires popstate.
override val handleBackNavigation: Boolean = false
override fun onWebViewCreate(webView: WebView) {
super.onWebViewCreate(webView)
backWebView = webView
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val wv = backWebView
if (wv != null) {
// Drive JS history.back() — it fires popstate (closes modals / navigates
// screens). WebView.goBack() would not, for same-document pushState entries.
wv.evaluateJavascript("window.history.back()", null)
} else {
isEnabled = false
onBackPressed()
isEnabled = true
}
}
})
}
private val saveFileLauncher = registerForActivityResult(
ActivityResultContracts.CreateDocument("application/gpx+xml")