feat(file): 添加文件下载权限管理和回退机制
- 实现 PublicDownloads 目录失败时自动回退到 Sandbox 目录 - 添加 Android 11+ 所有文件访问权限检查功能 - 提供跳转到文件访问权限设置页面的 Intent 方法 - 重构目录解析逻辑为独立的 resolveBaseDir 函数 - 增强文件下载的异常处理和日志记录 - 修复空白文件名处理逻辑,确保默认文件名为 download.bin
这个提交包含在:
父节点
2bd497ead4
当前提交
510bb8439b
@ -254,7 +254,28 @@ object FileSDK {
|
||||
android.util.Log.d("XWV", "saveBlobDownload: fileName=$fileName, dest=$destination, b64Len=${base64Data.length}")
|
||||
val bytes = android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT)
|
||||
android.util.Log.d("XWV", "saveBlobDownload: decoded ${bytes.size} bytes")
|
||||
val baseDir = when (destination) {
|
||||
|
||||
val safeName = fileName.takeIf { it.isNotBlank() } ?: "download.bin"
|
||||
|
||||
// 尝试目标目录,如果 PublicDownloads 失败则回退到 Sandbox
|
||||
val baseDir = resolveBaseDir(context, destination)
|
||||
val target = uniqueFile(baseDir, safeName)
|
||||
android.util.Log.d("XWV", "saveBlobDownload: writing to ${target.absolutePath}")
|
||||
return try {
|
||||
target.writeBytes(bytes)
|
||||
android.util.Log.d("XWV", "saveBlobDownload: done, size=${target.length()}")
|
||||
target
|
||||
} catch (e: java.io.IOException) {
|
||||
android.util.Log.w("XWV", "saveBlobDownload: primary dir failed (${e.message}), falling back to Sandbox")
|
||||
val fallbackDir = resolveBaseDir(context, FileDownloadDestination.Sandbox)
|
||||
val fallbackTarget = uniqueFile(fallbackDir, safeName)
|
||||
fallbackTarget.writeBytes(bytes)
|
||||
android.util.Log.d("XWV", "saveBlobDownload: fallback done, ${fallbackTarget.absolutePath}, size=${fallbackTarget.length()}")
|
||||
fallbackTarget
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveBaseDir(context: Context, destination: FileDownloadDestination): File = when (destination) {
|
||||
FileDownloadDestination.PublicDownloads -> {
|
||||
val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
|
||||
File(
|
||||
@ -262,22 +283,39 @@ object FileSDK {
|
||||
android.os.Environment.DIRECTORY_DOWNLOADS
|
||||
),
|
||||
appName,
|
||||
).apply {
|
||||
val created = mkdirs()
|
||||
android.util.Log.d("XWV", "saveBlobDownload: PublicDownloads dir=$absolutePath, created=$created, exists=${exists()}")
|
||||
}
|
||||
).apply { mkdirs() }
|
||||
}
|
||||
FileDownloadDestination.Sandbox -> {
|
||||
(context.getExternalFilesDir(null) ?: context.filesDir).also {
|
||||
android.util.Log.d("XWV", "saveBlobDownload: Sandbox dir=${it.absolutePath}")
|
||||
context.getExternalFilesDir(null) ?: context.filesDir
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否拥有"所有文件访问权限"(MANAGE_EXTERNAL_STORAGE)。
|
||||
* Android 11+ 需要此权限才能写入公共 Downloads 目录。
|
||||
* Android 10 及以下始终返回 true(使用 WRITE_EXTERNAL_STORAGE)。
|
||||
*/
|
||||
fun isManageStorageGranted(context: Context): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
Environment.isExternalStorageManager()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回跳转到"所有文件访问权限"设置页的 Intent。
|
||||
* 调用方应使用 startActivityForResult 或 registerForActivityResult 处理返回。
|
||||
*/
|
||||
fun requestManageStorageIntent(context: Context): Intent? {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return null
|
||||
return try {
|
||||
Intent(android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
|
||||
data = Uri.parse("package:${context.packageName}")
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
Intent(android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
||||
}
|
||||
val target = uniqueFile(baseDir, fileName.takeIf { it.isNotBlank() } ?: "download.bin")
|
||||
android.util.Log.d("XWV", "saveBlobDownload: writing to ${target.absolutePath}")
|
||||
target.writeBytes(bytes)
|
||||
android.util.Log.d("XWV", "saveBlobDownload: done, size=${target.length()}")
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户