Skip to content

Commit c8e5552

Browse files
RD-696: Add webview wrapper (#13)
1 parent d11f997 commit c8e5552

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2025, MapTiler
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD 3-Clause
5+
*/
6+
7+
package com.maptiler.maptilersdk.bridge
8+
9+
import android.content.Context
10+
import android.webkit.WebView
11+
import com.maptiler.maptilersdk.MTConfig
12+
import com.maptiler.maptilersdk.logging.MTLogLevel
13+
import com.maptiler.maptilersdk.logging.MTLogType
14+
import com.maptiler.maptilersdk.logging.MTLogger
15+
import kotlinx.coroutines.CompletableDeferred
16+
import kotlinx.coroutines.Dispatchers
17+
import kotlinx.coroutines.withContext
18+
19+
internal interface WebViewExecutorDelegate {
20+
fun onNavigationFinished(url: String)
21+
}
22+
23+
internal class WebViewExecutor(
24+
context: Context,
25+
) : WebViewManagerDelegate {
26+
private val exceptionKey = "WKJavaScriptExceptionMessage"
27+
private var webView: WebView? = null
28+
private val webViewManager: WebViewManager =
29+
WebViewManager(context).apply {
30+
delegate = this@WebViewExecutor
31+
}
32+
33+
var delegate: WebViewExecutorDelegate? = null
34+
35+
init {
36+
webView = webViewManager.getAttachableWebView()
37+
webView?.isVerticalScrollBarEnabled = false
38+
}
39+
40+
fun getWebView(): WebView? = webView
41+
42+
suspend fun execute(command: MTCommand): MTBridgeReturnType =
43+
withContext(Dispatchers.Main) {
44+
val webView = webView ?: throw MTError.BridgeNotLoaded
45+
val isVerbose = MTConfig.logLevel == MTLogLevel.Debug(true)
46+
47+
val deferred = CompletableDeferred<MTBridgeReturnType>()
48+
webView.evaluateJavascript(command.toJS()) { result ->
49+
try {
50+
if (result == null) {
51+
if (isVerbose) {
52+
MTLogger.log("$command completed with unsupported return type.", MTLogType.WARNING)
53+
}
54+
55+
deferred.complete(MTBridgeReturnType.UnsupportedType)
56+
} else {
57+
try {
58+
val parsedResult = MTBridgeReturnType.from(result)
59+
deferred.complete(parsedResult)
60+
} catch (e: Exception) {
61+
deferred.completeExceptionally(MTError.InvalidResultType(result))
62+
}
63+
}
64+
} catch (e: Exception) {
65+
if (isVerbose) {
66+
MTLogger.log("Bridging error occurred for $command: ${e.message}", MTLogType.ERROR)
67+
}
68+
69+
deferred.completeExceptionally(MTError.Unknown(e.message ?: "Unknown error"))
70+
}
71+
}
72+
73+
return@withContext deferred.await()
74+
}
75+
76+
override fun onNavigationFinished(url: String) {
77+
delegate?.onNavigationFinished(url)
78+
}
79+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2025, MapTiler
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD 3-Clause
5+
*/
6+
7+
package com.maptiler.maptilersdk.bridge
8+
9+
import android.annotation.SuppressLint
10+
import android.content.Context
11+
import android.webkit.WebChromeClient
12+
import android.webkit.WebView
13+
import android.webkit.WebViewClient
14+
15+
internal interface WebViewManagerDelegate {
16+
fun onNavigationFinished(url: String)
17+
}
18+
19+
@SuppressLint("SetJavaScriptEnabled")
20+
internal class WebViewManager(
21+
private val context: Context,
22+
) {
23+
object Constants {
24+
object Error {
25+
const val HANDLER = "errorHandler"
26+
const val MESSAGE = "message"
27+
const val UNKNOWN = "Unknown Error"
28+
}
29+
30+
object Map {
31+
const val HANDLER = "mapHandler"
32+
const val EVENT = "event"
33+
const val DATA = "data"
34+
}
35+
36+
object JSResources {
37+
const val MAPTILER_MAP = "MapTilerMap"
38+
const val MAPTILER_SDK = "maptiler-sdk.umd.min"
39+
const val MAPTILER_STYLESHEET = "maptiler-sdk"
40+
41+
const val HTML_EXTENSION = "html"
42+
const val JS_EXTENSION = "js"
43+
const val CSS_EXTENSION = "css"
44+
}
45+
}
46+
47+
var delegate: WebViewManagerDelegate? = null
48+
private var webView: WebView? = null
49+
50+
fun getAttachableWebView(): WebView {
51+
if (webView == null) {
52+
webView =
53+
WebView(context).apply {
54+
settings.javaScriptEnabled = true
55+
settings.allowFileAccess = false
56+
settings.allowContentAccess = false
57+
58+
webChromeClient = WebChromeClient()
59+
webViewClient =
60+
object : WebViewClient() {
61+
override fun onPageFinished(
62+
view: WebView?,
63+
url: String?,
64+
) {
65+
url?.let { delegate?.onNavigationFinished(it) }
66+
}
67+
}
68+
69+
loadUrl("file:///android_asset/${Constants.JSResources.MAPTILER_MAP}.${Constants.JSResources.HTML_EXTENSION}")
70+
}
71+
}
72+
73+
return webView!!
74+
}
75+
}

0 commit comments

Comments
 (0)