fix(webview): 恢复文件选择器使用 ACTION_GET_CONTENT 保持原有 UI

ACTION_OPEN_DOCUMENT 打开的是 SAF 文档选择器,缺少文档、图片、最近等
快捷入口,体验不及 ACTION_GET_CONTENT 的系统底部弹窗。

真正修复 vivo/OPPO URI 权限问题的是 copyUriToWebViewCache(),选择文件后
将内容复制到本地 file:// 路径再传给 WebView,与 Intent 类型无关。
故改回 ACTION_GET_CONTENT,恢复原有选择器界面。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-06-24 14:38:25 +08:00
父节点 7306041d79
当前提交 4f4f8564df

查看文件

@ -45,6 +45,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import android.provider.MediaStore
import com.xuqm.sdk.file.FileDownloadDestination
import com.xuqm.sdk.file.FileSDK
import kotlinx.coroutines.Dispatchers
@ -89,6 +90,10 @@ internal fun resolvePickerMimeTypes(acceptTypes: Array<String>): Array<String> {
// ACTION_GET_CONTENT contract supporting multiple MIME types via EXTRA_MIME_TYPES.
internal class GetContentWithMimeTypes : ActivityResultContract<Array<String>, Uri?>() {
override fun createIntent(context: Context, input: Array<String>): Intent =
// Use ACTION_GET_CONTENT to preserve the familiar system file picker UI (bottom sheet
// with Documents/Images/Recent shortcuts). URI permission issues on vivo/OPPO are
// handled downstream by copyUriToWebViewCache(), which copies the file to a local
// cache path before passing it to WebView — so the intent type does not matter.
Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
if (input.size == 1) {
@ -272,14 +277,14 @@ internal fun storagePermissionsGranted(context: Context, acceptMimes: Array<Stri
*/
internal fun copyUriToWebViewCache(context: Context, uri: Uri, mimeType: String?): Uri? {
return runCatching {
// Query display name; fall back to file path (DATA column) on vivo / OPPO devices where
// DISPLAY_NAME is not populated for content://media/external/file/... URIs.
// DISPLAY_NAME may be null on some OEM devices — fall back to the DATA column
// just for the filename, not for file access.
val displayName: String? = context.contentResolver.query(
uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null
)?.use { cursor -> if (cursor.moveToFirst()) cursor.getString(0) else null }
?: runCatching {
context.contentResolver.query(
uri, arrayOf(android.provider.MediaStore.MediaColumns.DATA), null, null, null
uri, arrayOf(MediaStore.MediaColumns.DATA), null, null, null
)?.use { cursor ->
if (cursor.moveToFirst()) cursor.getString(0)?.substringAfterLast('/') else null
}
@ -298,7 +303,7 @@ internal fun copyUriToWebViewCache(context: Context, uri: Uri, mimeType: String?
val bytesRead = context.contentResolver.openInputStream(uri)?.use { input ->
dest.outputStream().use { out -> input.copyTo(out) }
} ?: return null // openInputStream returned null — URI unreadable
} ?: return null
android.util.Log.d("XWV", "copyUriToWebViewCache: $uri${dest.name} ($bytesRead bytes)")
Uri.fromFile(dest)