diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/FileChooseWebViewSample.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/FileChooseWebViewSample.kt
new file mode 100644
index 00000000..ba7a9cad
--- /dev/null
+++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/FileChooseWebViewSample.kt
@@ -0,0 +1,68 @@
+package com.kevinnzou.sample
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Modifier
+import androidx.navigation.NavHostController
+import com.multiplatform.webview.util.KLogSeverity
+import com.multiplatform.webview.web.WebView
+import com.multiplatform.webview.web.rememberWebViewNavigator
+import com.multiplatform.webview.web.rememberWebViewStateWithHTMLFile
+
+/**
+ * Created By briandr97 2024/8/8
+ *
+ * Basic Sample for choose file in webview
+ */
+@Composable
+internal fun FileChooseWebViewSample(navHostController: NavHostController? = null) {
+ val webViewState = rememberWebViewStateWithHTMLFile(fileName = "fileChoose.html")
+ val webViewNavigator = rememberWebViewNavigator()
+ LaunchedEffect(Unit) {
+ webViewState.webSettings.apply {
+ zoomLevel = 1.0
+ logSeverity = KLogSeverity.Debug
+ androidWebSettings.apply {
+ isAlgorithmicDarkeningAllowed = true
+ safeBrowsingEnabled = true
+ allowFileAccess = false
+ }
+ }
+ }
+ MaterialTheme {
+ Column {
+ TopAppBar(
+ title = { Text(text = "Html Sample") },
+ navigationIcon = {
+ IconButton(onClick = {
+ navHostController?.popBackStack()
+ }) {
+ Icon(
+ imageVector = Icons.Default.ArrowBack,
+ contentDescription = "Back",
+ )
+ }
+ },
+ )
+
+ Box(Modifier.fillMaxSize()) {
+ WebView(
+ state = webViewState,
+ modifier = Modifier.fillMaxSize(),
+ captureBackPresses = false,
+ navigator = webViewNavigator,
+ )
+ }
+ }
+ }
+}
diff --git a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt
index 24d92ea4..cdfb1ea2 100644
--- a/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt
+++ b/sample/shared/src/commonMain/kotlin/com/kevinnzou/sample/WebViewApp.kt
@@ -50,6 +50,9 @@ internal fun WebViewApp() {
composable("intercept") {
InterceptRequestSample(controller)
}
+ composable("file") {
+ FileChooseWebViewSample(controller)
+ }
}
}
@@ -83,6 +86,12 @@ fun MainScreen(controller: NavController) {
}) {
Text("Intercept Request Sample", fontSize = 18.sp)
}
+ Spacer(modifier = Modifier.height(20.dp))
+ Button(onClick = {
+ controller.navigate("file")
+ }) {
+ Text("File Choose Sample", fontSize = 18.sp)
+ }
}
}
diff --git a/sample/shared/src/commonMain/resources/assets/fileChoose.html b/sample/shared/src/commonMain/resources/assets/fileChoose.html
new file mode 100644
index 00000000..c59b8543
--- /dev/null
+++ b/sample/shared/src/commonMain/resources/assets/fileChoose.html
@@ -0,0 +1,25 @@
+
+
+
+
+ Compose WebView Multiplatform
+
+
+Compose WebView Multiplatform
+
+ image:
+
+
+
+ video:
+
+
+
+ audio:
+
+
+
+
\ No newline at end of file
diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/FileChoosableWebView.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/FileChoosableWebView.kt
new file mode 100644
index 00000000..956cee05
--- /dev/null
+++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/FileChoosableWebView.kt
@@ -0,0 +1,128 @@
+package com.multiplatform.webview.web
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.Intent
+import android.net.Uri
+import android.webkit.ValueCallback
+import android.webkit.WebView
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import com.multiplatform.webview.jsbridge.WebViewJsBridge
+import com.multiplatform.webview.util.KLogger
+
+@Composable
+fun FileChoosableWebView(
+ state: WebViewState,
+ modifier: Modifier,
+ captureBackPresses: Boolean,
+ navigator: WebViewNavigator,
+ webViewJsBridge: WebViewJsBridge?,
+ onCreated: (NativeWebView) -> Unit,
+ onDispose: (NativeWebView) -> Unit,
+ factory: (WebViewFactoryParam) -> NativeWebView,
+) {
+ var fileChooserIntent by remember { mutableStateOf(null) }
+
+ val webViewChromeClient =
+ remember {
+ FileChoosableWebChromeClient(onShowFilePicker = { fileChooserIntent = it })
+ }
+
+ val launcher =
+ rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.StartActivityForResult(),
+ ) { result: ActivityResult ->
+ if (result.resultCode != Activity.RESULT_OK) {
+ KLogger.d { "resultCode is not RESULT_OK (value: ${result.resultCode})" }
+ webViewChromeClient.cancelFileChooser()
+ return@rememberLauncherForActivityResult
+ }
+
+ val intent = result.data
+ if (intent == null) {
+ KLogger.d { "result intent is null" }
+ webViewChromeClient.cancelFileChooser()
+ return@rememberLauncherForActivityResult
+ }
+
+ val singleFile: Uri? = intent.data
+ val multiFiles: List? = intent.getUris()
+
+ when {
+ singleFile != null -> webViewChromeClient.onReceiveFiles(arrayOf(singleFile))
+ multiFiles != null -> webViewChromeClient.onReceiveFiles(multiFiles.toTypedArray())
+ else -> {
+ KLogger.d { "data and clipData is null" }
+ webViewChromeClient.cancelFileChooser()
+ }
+ }
+ }
+
+ LaunchedEffect(key1 = fileChooserIntent) {
+ if (fileChooserIntent != null) {
+ try {
+ launcher.launch(fileChooserIntent)
+ } catch (e: ActivityNotFoundException) {
+ webViewChromeClient.cancelFileChooser()
+ }
+ }
+ }
+
+ AccompanistWebView(
+ state,
+ modifier,
+ captureBackPresses,
+ navigator,
+ webViewJsBridge,
+ onCreated = onCreated,
+ onDispose = onDispose,
+ factory = { factory(WebViewFactoryParam(it)) },
+ chromeClient = webViewChromeClient,
+ )
+}
+
+private fun Intent.getUris(): List? {
+ val clipData = clipData ?: return null
+ return (0 until clipData.itemCount).map { clipData.getItemAt(it).uri }
+}
+
+class FileChoosableWebChromeClient(
+ private val onShowFilePicker: (Intent) -> Unit,
+) : AccompanistWebChromeClient() {
+ private var filePathCallback: ValueCallback>? = null
+
+ override fun onShowFileChooser(
+ webView: WebView?,
+ filePathCallback: ValueCallback>?,
+ fileChooserParams: FileChooserParams?,
+ ): Boolean {
+ this.filePathCallback = filePathCallback
+ val filePickerIntent = fileChooserParams?.createIntent()
+
+ if (filePickerIntent == null) {
+ cancelFileChooser()
+ } else {
+ onShowFilePicker(filePickerIntent)
+ }
+ return true
+ }
+
+ fun onReceiveFiles(uris: Array) {
+ filePathCallback?.onReceiveValue(uris)
+ filePathCallback = null
+ }
+
+ fun cancelFileChooser() {
+ filePathCallback?.onReceiveValue(null)
+ filePathCallback = null
+ }
+}
diff --git a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt
index f20b4c68..b5c30bec 100644
--- a/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt
+++ b/webview/src/androidMain/kotlin/com/multiplatform/webview/web/WebView.android.kt
@@ -19,7 +19,7 @@ actual fun ActualWebView(
onDispose: (NativeWebView) -> Unit,
factory: (WebViewFactoryParam) -> NativeWebView,
) {
- AccompanistWebView(
+ FileChoosableWebView(
state,
modifier,
captureBackPresses,
@@ -27,7 +27,7 @@ actual fun ActualWebView(
webViewJsBridge,
onCreated = onCreated,
onDispose = onDispose,
- factory = { factory(WebViewFactoryParam(it)) },
+ factory = factory,
)
}