feat(webview): 添加blob下载功能并完善存储权限处理
- 添加pendingBlobDownload状态管理blob下载请求 - 实现API 30+系统上MANAGE_EXTERNAL_STORAGE权限检查 - 添加blob数据解码和文件保存功能 - 集成下载完成事件分发机制 - 完善存储权限对话框中的状态清理逻辑 - 实现blob下载成功后的相册保存或文件打开操作
这个提交包含在:
父节点
aca0d009a8
当前提交
f0276bb440
@ -231,6 +231,8 @@ fun XWebViewView(
|
|||||||
var showImageSourceDialog by remember { mutableStateOf(false) }
|
var showImageSourceDialog by remember { mutableStateOf(false) }
|
||||||
var showStoragePermissionDialog by remember { mutableStateOf(false) }
|
var showStoragePermissionDialog by remember { mutableStateOf(false) }
|
||||||
val pendingUrlDownload = remember { mutableStateOf<Pair<String, String?>?>(null) }
|
val pendingUrlDownload = remember { mutableStateOf<Pair<String, String?>?>(null) }
|
||||||
|
// blobdownload pending: (url, filename, base64Data)
|
||||||
|
val pendingBlobDownload = remember { mutableStateOf<Triple<String, String, String>?>(null) }
|
||||||
|
|
||||||
val notificationPermissionLauncher = rememberLauncherForActivityResult(
|
val notificationPermissionLauncher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.RequestPermission()
|
ActivityResultContracts.RequestPermission()
|
||||||
@ -304,6 +306,15 @@ fun XWebViewView(
|
|||||||
android.util.Log.e("XWV", "blobdownload: empty data")
|
android.util.Log.e("XWV", "blobdownload: empty data")
|
||||||
return@handler
|
return@handler
|
||||||
}
|
}
|
||||||
|
// On API 30+, writing to public Downloads requires MANAGE_EXTERNAL_STORAGE.
|
||||||
|
if (config.downloadDestination == FileDownloadDestination.PublicDownloads &&
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
|
||||||
|
!FileSDK.isManageStorageGranted(context)
|
||||||
|
) {
|
||||||
|
pendingBlobDownload.value = Triple(url, filename, b64)
|
||||||
|
showStoragePermissionDialog = true
|
||||||
|
return@handler
|
||||||
|
}
|
||||||
android.util.Log.d("XWV", "blobdownload: filename=$filename, b64Len=${b64.length}, dest=${config.downloadDestination}")
|
android.util.Log.d("XWV", "blobdownload: filename=$filename, b64Len=${b64.length}, dest=${config.downloadDestination}")
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -402,6 +413,7 @@ fun XWebViewView(
|
|||||||
val observer = LifecycleEventObserver { _, event ->
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
if (event == Lifecycle.Event.ON_RESUME) {
|
if (event == Lifecycle.Event.ON_RESUME) {
|
||||||
val pd = pendingUrlDownload.value
|
val pd = pendingUrlDownload.value
|
||||||
|
val pbd = pendingBlobDownload.value
|
||||||
if (pd != null && FileSDK.isManageStorageGranted(context)) {
|
if (pd != null && FileSDK.isManageStorageGranted(context)) {
|
||||||
val (url, filename) = pd
|
val (url, filename) = pd
|
||||||
pendingUrlDownload.value = null
|
pendingUrlDownload.value = null
|
||||||
@ -436,6 +448,27 @@ fun XWebViewView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pbd != null && FileSDK.isManageStorageGranted(context)) {
|
||||||
|
val (url, filename, b64) = pbd
|
||||||
|
pendingBlobDownload.value = null
|
||||||
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
|
runCatching {
|
||||||
|
FileSDK.saveBlobDownload(context, b64, filename, config.downloadDestination)
|
||||||
|
}.onSuccess { file ->
|
||||||
|
android.util.Log.d("XWV", "blobdownload saved: ${file.absolutePath}, size=${file.length()}")
|
||||||
|
val savedToGallery = runCatching { FileSDK.saveImageToGallery(context, file) }.getOrDefault(false)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
dispatchDownloadEvent("__xwvDownloadDone", url, ",success:true")
|
||||||
|
if (!savedToGallery) FileSDK.openFile(context, file)
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
android.util.Log.e("XWV", "blobdownload FAILED", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
dispatchDownloadEvent("__xwvDownloadDone", url, ",success:false,error:'${e.message?.escapeJs()}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleOwner.lifecycle.addObserver(observer)
|
lifecycleOwner.lifecycle.addObserver(observer)
|
||||||
@ -564,6 +597,7 @@ fun XWebViewView(
|
|||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
showStoragePermissionDialog = false
|
showStoragePermissionDialog = false
|
||||||
pendingUrlDownload.value = null
|
pendingUrlDownload.value = null
|
||||||
|
pendingBlobDownload.value = null
|
||||||
},
|
},
|
||||||
title = { Text("需要存储权限") },
|
title = { Text("需要存储权限") },
|
||||||
text = { Text("下载文件需要「所有文件访问权限」,请在设置中授权后将自动继续下载。") },
|
text = { Text("下载文件需要「所有文件访问权限」,请在设置中授权后将自动继续下载。") },
|
||||||
@ -577,6 +611,7 @@ fun XWebViewView(
|
|||||||
TextButton(onClick = {
|
TextButton(onClick = {
|
||||||
showStoragePermissionDialog = false
|
showStoragePermissionDialog = false
|
||||||
pendingUrlDownload.value = null
|
pendingUrlDownload.value = null
|
||||||
|
pendingBlobDownload.value = null
|
||||||
}) { Text("取消") }
|
}) { Text("取消") }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户