feat(activity): 添加 Zip 文件下载解压工具和趣味页面
- 新增 ZipDownloadUtils:支持大文件下载、进度回调、断点续传 - 新增 ZipExtractUtils:支持 Zip 文件解压、进度回调、自动创建目录 - 新增 ModelResourceManager:高层资源管理器,协调下载和解压流程 - 新增 FunActivity:趣味测试页面,包含 3 个按钮 - 按钮 1:下载 Zip 文件 - 按钮 2:解压 Zip 文件 - 按钮 3:读取 JSON 并保存到 ObjectBox 数据库 - 新增 activity_fun.xml:趣味页面布局 - 新增 ZipToolsUsageExample:工具使用示例文档 - 改进 ToastUtils:改为 Context 扩展方法 (toast/toastLong) - 暂时注释 HomeActivity 中 tvTime 的点击事件
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
package com.sw.inbound.utils
|
||||
|
||||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* 模型资源管理器
|
||||
* 负责从接口下载模型资源包(Zip),并解压到应用缓存目录
|
||||
* 替代原有的 assets 中写死的 data 文件夹和 .pt 文件
|
||||
*/
|
||||
object ModelResourceManager {
|
||||
|
||||
/**
|
||||
* 资源下载和解压的回调接口
|
||||
*/
|
||||
interface ResourceCallback {
|
||||
/**
|
||||
* 下载进度回调
|
||||
* @param progress 进度百分比 (0-100)
|
||||
* @param message 进度信息
|
||||
*/
|
||||
fun onDownloadProgress(progress: Int, message: String)
|
||||
|
||||
/**
|
||||
* 解压进度回调
|
||||
* @param progress 进度百分比 (0-100)
|
||||
* @param message 进度信息
|
||||
*/
|
||||
fun onExtractProgress(progress: Int, message: String)
|
||||
|
||||
/**
|
||||
* 完成回调
|
||||
* @param resourceDir 资源所在目录
|
||||
*/
|
||||
fun onSuccess(resourceDir: File)
|
||||
|
||||
/**
|
||||
* 失败回调
|
||||
* @param errorMessage 错误信息
|
||||
*/
|
||||
fun onFailure(errorMessage: String)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载并解压模型资源
|
||||
* @param context 应用上下文
|
||||
* @param downloadUrl 资源包下载链接
|
||||
* @param callback 进度和结果回调
|
||||
*/
|
||||
fun downloadAndExtractResources(
|
||||
context: Context,
|
||||
downloadUrl: String,
|
||||
callback: ResourceCallback
|
||||
) {
|
||||
try {
|
||||
// 获取缓存目录
|
||||
val cacheDir = context.cacheDir
|
||||
val resourcesDir = File(cacheDir, "model_resources")
|
||||
val zipFile = File(cacheDir, "model_resources.zip")
|
||||
|
||||
Timber.d("资源缓存目录: ${resourcesDir.absolutePath}")
|
||||
Timber.d("Zip 文件路径: ${zipFile.absolutePath}")
|
||||
|
||||
// Step 1: 下载 Zip 文件
|
||||
Timber.d("开始下载资源包: $downloadUrl")
|
||||
ZipDownloadUtils.downloadZip(
|
||||
downloadUrl = downloadUrl,
|
||||
targetFile = zipFile,
|
||||
listener = object : ZipDownloadUtils.DownloadProgressListener {
|
||||
override fun onProgress(currentSize: Long, totalSize: Long, progress: Int) {
|
||||
val message = "下载中... ${formatFileSize(currentSize)}/${formatFileSize(totalSize)}"
|
||||
callback.onDownloadProgress(progress, message)
|
||||
}
|
||||
|
||||
override fun onSuccess(file: File) {
|
||||
Timber.d("资源包下载完成")
|
||||
callback.onDownloadProgress(100, "下载完成")
|
||||
|
||||
// Step 2: 解压 Zip 文件
|
||||
extractResources(resourcesDir, zipFile, callback)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
val errorMsg = "资源包下载失败: ${exception.message}"
|
||||
Timber.e(exception, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
val errorMsg = "资源下载和解压过程异常: ${e.message}"
|
||||
Timber.e(e, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压资源包
|
||||
*/
|
||||
private fun extractResources(
|
||||
resourcesDir: File,
|
||||
zipFile: File,
|
||||
callback: ResourceCallback
|
||||
) {
|
||||
try {
|
||||
Timber.d("开始解压资源包")
|
||||
ZipExtractUtils.extractZip(
|
||||
zipFile = zipFile,
|
||||
targetDir = resourcesDir,
|
||||
listener = object : ZipExtractUtils.ExtractProgressListener {
|
||||
override fun onProgress(
|
||||
currentFile: String,
|
||||
currentIndex: Int,
|
||||
totalFiles: Int,
|
||||
progress: Int
|
||||
) {
|
||||
val message = "解压中... ($currentIndex/$totalFiles) $currentFile"
|
||||
callback.onExtractProgress(progress, message)
|
||||
}
|
||||
|
||||
override fun onSuccess(targetDir: File) {
|
||||
Timber.d("资源包解压完成: ${targetDir.absolutePath}")
|
||||
callback.onExtractProgress(100, "解压完成")
|
||||
|
||||
// 清理 Zip 文件
|
||||
zipFile.delete()
|
||||
Timber.d("临时 Zip 文件已删除")
|
||||
|
||||
callback.onSuccess(targetDir)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
val errorMsg = "资源包解压失败: ${exception.message}"
|
||||
Timber.e(exception, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
val errorMsg = "解压过程异常: ${e.message}"
|
||||
Timber.e(e, errorMsg)
|
||||
callback.onFailure(errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已缓存的资源目录
|
||||
* @param context 应用上下文
|
||||
* @return 资源目录,如果不存在则返回 null
|
||||
*/
|
||||
fun getCachedResourcesDir(context: Context): File? {
|
||||
val resourcesDir = File(context.cacheDir, "model_resources")
|
||||
return if (resourcesDir.exists() && resourcesDir.isDirectory) {
|
||||
resourcesDir
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存的资源
|
||||
* @param context 应用上下文
|
||||
* @return 是否清理成功
|
||||
*/
|
||||
fun clearCachedResources(context: Context): Boolean {
|
||||
return try {
|
||||
val resourcesDir = File(context.cacheDir, "model_resources")
|
||||
val zipFile = File(context.cacheDir, "model_resources.zip")
|
||||
|
||||
val dirDeleted = resourcesDir.deleteRecursively()
|
||||
val zipDeleted = zipFile.delete()
|
||||
|
||||
Timber.d("清理缓存资源: 目录=$dirDeleted, Zip=$zipDeleted")
|
||||
dirDeleted || zipDeleted
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "清理缓存资源失败")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小为可读字符串
|
||||
*/
|
||||
private fun formatFileSize(bytes: Long): String {
|
||||
return when {
|
||||
bytes <= 0 -> "0 B"
|
||||
bytes < 1024 -> "$bytes B"
|
||||
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
|
||||
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
|
||||
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user