'How to upload multiple files (webview) in kotlin in 2021?
I am trying to enable multiple images upload on my WKWebView in Android (Kotlin). My web page has an input type file with the multiple attribute. I searched several similar questions which sometime are quite old and could not find one that resolves my problem.
I tried to add:
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
Then I also found an answer using an Array like this, which seems to make sense
filePathCallback: ValueCallback<Array<Uri>>?
However neither worked in the end. I am posting my cleaned-up code as it is now. I can select pictures one by one but not several at a time (note that this works well if I access the URL directly in a browser on Android or on my iOS app..).
package foo.bar.app
import android.Manifest
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.net.http.SslError
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.webkit.*
import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.onesignal.OneSignal
open class WebActivity : AppCompatActivity() {
companion object {
const val PAGE_URL = "pageUrl"
const val MAX_PROGRESS = 100
// File selector
private var mUploadMessage: ValueCallback<Uri>? = null
private var uploadMessage: ValueCallback<Array<Uri>>? = null
const val FILECHOOSER_RESULTCODE = 1
const val REQUEST_SELECT_FILE = 100
fun newIntent(context: Context, pageUrl: String): Intent {
val intent = Intent(context, WebActivity::class.java)
intent.putExtra(PAGE_URL, pageUrl)
return intent
}
}
var pageUrl: String = ""
private lateinit var binding: ActivityWebBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityWebBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
pageUrl = intent.getStringExtra(PAGE_URL) ?: ""
initWebView()
setWebClient()
loadUrl(appLinkUrl, "")
}
private fun initWebView() {
binding.webView.settings.loadWithOverviewMode = true
binding.webView.settings.useWideViewPort = true
binding.webView.settings.domStorageEnabled = true
binding.webView.settings.databaseEnabled = true
binding.webView.webViewClient = object : WebViewClient() {
override
fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
when (error!!.primaryError) {
SslError.SSL_UNTRUSTED -> handler?.cancel()
SslError.SSL_EXPIRED -> handler?.cancel()
SslError.SSL_IDMISMATCH -> handler?.cancel()
SslError.SSL_NOTYETVALID -> handler?.cancel()
}
//handler?.proceed()
}
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url: String = request?.url.toString()
binding.webView.loadUrl(url)
return true
}
// for backward compatibility
override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
binding.webView.loadUrl(url)
return true
}
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
}
}
}
private fun setWebClient() {
binding.webView.webChromeClient = object : WebChromeClient() {
// File selector
// For Android 4.1
fun openFileChooser(uploadMsg: ValueCallback<Uri>, acceptType: String, capture: String) {
mUploadMessage = uploadMsg
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "image/*"
[email protected](Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE)
}
// File selector
protected fun openFileChooser(uploadMsg: ValueCallback<Uri>) {
mUploadMessage = uploadMsg
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE)
}
// File selector
override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
uploadMessage?.onReceiveValue(null)
uploadMessage = null
uploadMessage = filePathCallback
val intent = fileChooserParams!!.createIntent()
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
try {
startActivityForResult(intent, REQUEST_SELECT_FILE)
return true
} catch (e: ActivityNotFoundException) {
uploadMessage = null
Toast.makeText(applicationContext, "Cannot Open File Chooser", Toast.LENGTH_LONG).show()
return false
}
return true
}
}
}
// File selector
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return
print("result code = $resultCode")
val results: Array<Uri>? = WebChromeClient.FileChooserParams.parseResult(resultCode, data)
uploadMessage?.onReceiveValue(results)
uploadMessage = null
}
} else if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return
// Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
// Use RESULT_OK only if you're implementing WebView inside an Activity
val result = if (intent == null || resultCode != RESULT_OK) null else intent.data
mUploadMessage?.onReceiveValue(result)
mUploadMessage = null
} else
Toast.makeText(applicationContext, "Failed to Upload Image", Toast.LENGTH_LONG).show()
}
private fun loadUrl(pageUrl: String, postData: String) {
if( postData == "" ) binding.webView.loadUrl(pageUrl)
else binding.webView.postUrl(pageUrl, postData.toByteArray())
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleIntent(intent)
}
private fun handleIntent(intent: Intent) {
val appLinkAction = intent.action
val appLinkData: Uri? = intent.data
if (Intent.ACTION_VIEW == appLinkAction) {
if (appLinkData != null) {
appLinkData.path?.let { loadUrl(it, "") }
}
}
}
}
I am trying to manage older Android versions, my objective is to support multiple file uploads in more recents versions at least. I can live without this feature on older versions.
Let me know if you see something I am missing.
Thanks for the help !
C
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
