From f806df360f9f789a8b5fe577cf5eab918ee3ed0c Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Tue, 24 Dec 2024 12:27:36 +0200 Subject: [PATCH 1/2] Feat: add ability to register custom JavascriptInterface --- .../java/com/novage/p2pml/P2PMediaLoader.kt | 28 +++++++++++++++++-- .../p2pml/webview/JavaScriptInterface.kt | 9 ++---- .../novage/p2pml/webview/WebViewManager.kt | 26 +++++++++++++++-- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/p2pml/src/main/java/com/novage/p2pml/P2PMediaLoader.kt b/p2pml/src/main/java/com/novage/p2pml/P2PMediaLoader.kt index 7db0cbe..a0298e8 100644 --- a/p2pml/src/main/java/com/novage/p2pml/P2PMediaLoader.kt +++ b/p2pml/src/main/java/com/novage/p2pml/P2PMediaLoader.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking /** @@ -37,6 +38,7 @@ import kotlinx.coroutines.runBlocking class P2PMediaLoader private constructor( private val coreConfigJson: String, private val serverPort: Int, + private val customJavaScriptInterfaces: List>, private val customEngineImplementationPath: String?, ) { private val engineStateManager = P2PStateManager() @@ -78,6 +80,7 @@ class P2PMediaLoader private constructor( scope!!, engineStateManager, playbackCalculator, + customJavaScriptInterfaces, onPageLoadFinished = { onWebViewLoaded() }, ) @@ -175,9 +178,11 @@ class P2PMediaLoader private constructor( manifestParser.reset() } - private suspend fun onWebViewLoaded() { - webViewManager?.initCoreEngine(coreConfigJson) - webViewLoadCompletion?.complete(Unit) + private fun onWebViewLoaded() { + scope?.launch { + webViewManager?.initCoreEngine(coreConfigJson) + webViewLoadCompletion?.complete(Unit) + } } private fun onServerStarted() { @@ -198,6 +203,7 @@ class P2PMediaLoader private constructor( private var coreConfig: String = "" private var serverPort: Int = Constants.DEFAULT_SERVER_PORT private var customEngineImplementationPath: String? = null + private var customJavaScriptInterfaces: MutableList> = mutableListOf() /** * Sets core P2P configurations. See [P2PML Core Config](https://novage.github.io/p2p-media-loader/docs/v2.1.0/types/p2p_media_loader_core.CoreConfig.html) @@ -221,6 +227,21 @@ class P2PMediaLoader private constructor( this.customEngineImplementationPath = path } + /** + * Adds a custom JavaScript interface to the WebView. + * The feature has to be used with custom engine implementation. + * methods has to be annotated with @JavascriptInterface. + * + * @param name Interface name + * @param obj Object with methods annotated with @JavascriptInterface + */ + fun addCustomJavaScriptInterface( + name: String, + obj: Any, + ) = apply { + customJavaScriptInterfaces.add(Pair(name, obj)) + } + /** * @return A new [P2PMediaLoader] instance. */ @@ -228,6 +249,7 @@ class P2PMediaLoader private constructor( P2PMediaLoader( coreConfig, serverPort, + customJavaScriptInterfaces, customEngineImplementationPath, ) } diff --git a/p2pml/src/main/java/com/novage/p2pml/webview/JavaScriptInterface.kt b/p2pml/src/main/java/com/novage/p2pml/webview/JavaScriptInterface.kt index a8c342e..2df4245 100644 --- a/p2pml/src/main/java/com/novage/p2pml/webview/JavaScriptInterface.kt +++ b/p2pml/src/main/java/com/novage/p2pml/webview/JavaScriptInterface.kt @@ -1,17 +1,12 @@ package com.novage.p2pml.webview import android.webkit.JavascriptInterface -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch internal class JavaScriptInterface( - private val coroutineScope: CoroutineScope, - private val onFullyLoadedCallback: suspend () -> Unit, + private val onFullyLoadedCallback: () -> Unit, ) { @JavascriptInterface fun onWebViewLoaded() { - coroutineScope.launch { - onFullyLoadedCallback() - } + onFullyLoadedCallback() } } diff --git a/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt b/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt index 1757a95..6c2c289 100644 --- a/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt +++ b/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.util.Log import android.view.View +import android.webkit.JavascriptInterface import android.webkit.WebView import androidx.annotation.OptIn import androidx.media3.common.util.UnstableApi @@ -22,13 +23,15 @@ import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +@SuppressLint("JavascriptInterface") @OptIn(UnstableApi::class) internal class WebViewManager( context: Context, private val coroutineScope: CoroutineScope, private val engineStateManager: P2PStateManager, private val playbackCalculator: ExoPlayerPlaybackCalculator, - onPageLoadFinished: suspend () -> Unit, + private val customJavaScriptInterfaces: List>, + private val onPageLoadFinished: () -> Unit, ) { @SuppressLint("SetJavaScriptEnabled") private val webView = @@ -38,7 +41,7 @@ internal class WebViewManager( webViewClient = WebViewClientCompat() visibility = View.GONE addJavascriptInterface( - JavaScriptInterface(coroutineScope, onPageLoadFinished), + JavaScriptInterface(onPageLoadFinished), "Android", ) } @@ -46,6 +49,25 @@ internal class WebViewManager( private var playbackInfoJob: Job? = null + init { + customJavaScriptInterfaces.forEach { (name, obj) -> + val isValid = validateJavaScriptInterface(obj) + if (!isValid) { + Log.e( + "WebViewManager", + "Object $obj does not have any methods annotated with @JavascriptInterface", + ) + return@forEach + } + webView.addJavascriptInterface(obj, name) + } + } + + private fun validateJavaScriptInterface(obj: Any): Boolean { + val methods = obj::class.java.methods + return methods.any { it.isAnnotationPresent(JavascriptInterface::class.java) } + } + private fun startPlaybackInfoUpdate() { if (playbackInfoJob !== null) return From c023efba9f2ccfcaa0f6abfcb252a621c8e5ff63 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Tue, 24 Dec 2024 12:28:08 +0200 Subject: [PATCH 2/2] Feat: add ability to register custom JavascriptInterface --- p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt b/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt index 6c2c289..29fa7bb 100644 --- a/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt +++ b/p2pml/src/main/java/com/novage/p2pml/webview/WebViewManager.kt @@ -30,7 +30,7 @@ internal class WebViewManager( private val coroutineScope: CoroutineScope, private val engineStateManager: P2PStateManager, private val playbackCalculator: ExoPlayerPlaybackCalculator, - private val customJavaScriptInterfaces: List>, + customJavaScriptInterfaces: List>, private val onPageLoadFinished: () -> Unit, ) { @SuppressLint("SetJavaScriptEnabled")