fix: enable WebView file uploads

This commit is contained in:
2026-04-15 15:18:36 -05:00
parent e754880233
commit a569e7b0d7
3 changed files with 79 additions and 5 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ android {
applicationId = "pro.relaytv"
minSdk = 26
targetSdk = 35
versionCode = 4
versionName = "1.2.2"
versionCode = 5
versionName = "1.2.3"
}
buildTypes {
+73 -1
View File
@@ -2,6 +2,7 @@ package pro.relaytv
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.ConnectivityManager
@@ -18,6 +19,7 @@ import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import android.webkit.ValueCallback
import android.widget.ArrayAdapter
import android.widget.LinearLayout
import android.widget.EditText
@@ -44,6 +46,18 @@ class MainActivity : AppCompatActivity() {
private val requestNotificationPermission =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { /* no-op */ }
private val webFileChooser =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val callback = pendingWebFileCallback ?: return@registerForActivityResult
pendingWebFileCallback = null
val uris = if (result.resultCode == Activity.RESULT_OK) {
parseFileChooserResult(result.data)
} else {
null
}
callback.onReceiveValue(uris)
}
private val uiHandler = Handler(Looper.getMainLooper())
private val connectivityManager by lazy { getSystemService(ConnectivityManager::class.java) }
@@ -59,6 +73,7 @@ class MainActivity : AppCompatActivity() {
private var networkCallbackRegistered = false
private var isInForeground = false
private var mdnsScanner: NsdRelayTvScanner? = null
private var pendingWebFileCallback: ValueCallback<Array<Uri>>? = null
private val heartbeatRunnable = Runnable { runHeartbeat() }
@@ -122,7 +137,27 @@ class MainActivity : AppCompatActivity() {
configureWebView()
web.webChromeClient = WebChromeClient()
web.webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
pendingWebFileCallback?.onReceiveValue(null)
pendingWebFileCallback = filePathCallback
val intent = buildFileChooserIntent(fileChooserParams)
return try {
webFileChooser.launch(intent)
true
} catch (_: Exception) {
pendingWebFileCallback = null
filePathCallback.onReceiveValue(null)
Toast.makeText(this@MainActivity, getString(R.string.file_picker_open_failed), Toast.LENGTH_SHORT).show()
false
}
}
}
web.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
mainFrameFailed = false
@@ -149,6 +184,43 @@ class MainActivity : AppCompatActivity() {
loadServerBase(base.trimEnd('/'), forcePickerOnFailure = true, manualRefresh = false)
}
private fun buildFileChooserIntent(fileChooserParams: WebChromeClient.FileChooserParams): Intent {
val acceptTypes = fileChooserParams.acceptTypes
.orEmpty()
.flatMap { it.split(",") }
.map { it.trim().lowercase() }
.filter { it.isNotBlank() }
val mimeTypes = acceptTypes
.filter { !it.startsWith(".") && "/" in it }
.distinct()
.ifEmpty { listOf("*/*") }
.toTypedArray()
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = if (mimeTypes.size == 1) mimeTypes.first() else "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
putExtra(
Intent.EXTRA_ALLOW_MULTIPLE,
fileChooserParams.mode == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE
)
}
return Intent.createChooser(intent, fileChooserParams.title ?: getString(R.string.select_file))
}
private fun parseFileChooserResult(data: Intent?): Array<Uri>? {
if (data == null) return null
val clipData = data.clipData
if (clipData != null && clipData.itemCount > 0) {
return Array(clipData.itemCount) { index -> clipData.getItemAt(index).uri }
.filterNotNull()
.takeIf { it.isNotEmpty() }
?.toTypedArray()
}
return data.data?.let { arrayOf(it) }
}
@SuppressLint("SetJavaScriptEnabled")
private fun configureWebView() {
val versionName = runCatching {
+4 -2
View File
@@ -9,6 +9,8 @@
<string name="server_name">Name</string>
<string name="server_url">Base URL</string>
<string name="reload">Reload</string>
<string name="select_file">Select file</string>
<string name="file_picker_open_failed">Couldn\'t open file picker.</string>
<string name="scan_lan">Scan LAN</string>
<string name="scanning_lan">Scanning LAN…</string>
<string name="discovered_servers">Discovered RelayTV Servers</string>
@@ -21,8 +23,8 @@
<string name="status_offline">Offline</string>
<string name="status_checking">Checking…</string>
<string name="status_active_prefix">Active</string>
<string name="share_queue_label">RelayTV Queue</string>
<string name="share_play_now_label">RelayTV Play</string>
<string name="share_queue_label">Queue</string>
<string name="share_play_now_label">Play</string>
<string name="share_queue_toast">Queued on "%s"</string>
<string name="share_play_now_toast">Playing now on "%s"</string>
<string name="share_media_queue_started">Uploading media to "%s"</string>