77package com.maptiler.maptilersdk.bridge
88
99import android.content.Context
10+ import android.view.View
1011import android.view.ViewGroup
1112import android.webkit.WebChromeClient
1213import android.webkit.WebSettings
@@ -52,6 +53,8 @@ internal class WebViewExecutor(
5253 }
5354
5455 private var _webView : WebView ? = null
56+ private var hasLoadedContent: Boolean = false
57+ private var jsInterfaceAdded: Boolean = false
5558 internal val webView: WebView
5659 get() {
5760 if (_webView == null ) {
@@ -69,6 +72,8 @@ internal class WebViewExecutor(
6972 WebView (context).apply {
7073 settings.javaScriptEnabled = true
7174 settings.allowFileAccess = true
75+ settings.allowFileAccessFromFileURLs = true
76+ settings.allowUniversalAccessFromFileURLs = true
7277 settings.domStorageEnabled = true
7378 settings.cacheMode = WebSettings .LOAD_CACHE_ELSE_NETWORK
7479 settings.useWideViewPort = true
@@ -93,13 +98,55 @@ internal class WebViewExecutor(
9398 ViewGroup .LayoutParams .MATCH_PARENT ,
9499 )
95100
96- loadUrl(" file:///android_asset/${Constants .JSResources .MAPTILER_MAP } .${Constants .JSResources .HTML_EXTENSION } " )
101+ // Defer loading until JS interface is attached and the view is attached.
102+ // This prevents early page load causing "Android is not defined" on some devices.
103+ if (isAttachedToWindow) {
104+ ensureContentLoadedIfReady(this )
105+ } else {
106+ addOnAttachStateChangeListener(
107+ object : View .OnAttachStateChangeListener {
108+ override fun onViewAttachedToWindow (v : View ) {
109+ ensureContentLoadedIfReady(this @apply)
110+ removeOnAttachStateChangeListener(this )
111+ }
112+
113+ override fun onViewDetachedFromWindow (v : View ) = Unit
114+ },
115+ )
116+ }
97117 }
98118 }
99119 }
100120
101121 internal fun setWebView (webView : WebView ) {
102122 this ._webView = webView
123+ // Ensure our clients are attached so navigation callbacks work consistently.
124+ webView.webChromeClient = WebChromeClient ()
125+ webView.webViewClient =
126+ object : WebViewClient () {
127+ override fun onPageFinished (
128+ view : WebView ? ,
129+ url : String? ,
130+ ) {
131+ url?.let { delegate?.onNavigationFinished(it) }
132+ }
133+ }
134+
135+ // If provided WebView is already attached, try loading if ready.
136+ if (webView.isAttachedToWindow) {
137+ ensureContentLoadedIfReady(webView)
138+ } else {
139+ webView.addOnAttachStateChangeListener(
140+ object : View .OnAttachStateChangeListener {
141+ override fun onViewAttachedToWindow (v : View ) {
142+ ensureContentLoadedIfReady(webView)
143+ webView.removeOnAttachStateChangeListener(this )
144+ }
145+
146+ override fun onViewDetachedFromWindow (v : View ) = Unit
147+ },
148+ )
149+ }
103150 }
104151
105152 internal fun getAttachableWebView (): WebView ? = webView
@@ -151,9 +198,21 @@ internal class WebViewExecutor(
151198
152199 internal fun addJSInterface (jsInterface : MTJavaScriptInterface ) {
153200 webView.addJavascriptInterface(jsInterface, " Android" )
201+ jsInterfaceAdded = true
202+ // Attempt to load content now that the interface is ready.
203+ ensureContentLoadedIfReady(webView)
154204 }
155205
156206 internal fun reload () {
157207 webView.reload()
158208 }
209+
210+ private fun ensureContentLoadedIfReady (webView : WebView ) {
211+ if (! hasLoadedContent && jsInterfaceAdded && webView.isAttachedToWindow) {
212+ hasLoadedContent = true
213+ webView.loadUrl(
214+ " file:///android_asset/${Constants .JSResources .MAPTILER_MAP } .${Constants .JSResources .HTML_EXTENSION } " ,
215+ )
216+ }
217+ }
159218}
0 commit comments