Files
Inbound20260114/app/src/main/java/com/sw/inbound/activity/FunActivity.kt
T
lvmeng 75306af483 fix: 修正 Toast 扩展方法导入和更新下载地址
- 恢复 ToastUtils.kt 为原始注释状态(Compose 版本)
- 修正 FunActivity 导入,使用 Common.kt 中已定义的 Context.toast 扩展方法
- 更新下载地址为真实的资源包地址
2026-03-13 14:16:16 +08:00

299 lines
11 KiB
Kotlin

package com.sw.inbound.activity
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.sw.inbound.base.BaseActivity
import com.sw.inbound.databinding.ActivityFunBinding
import com.sw.inbound.objbox.Food
import com.sw.inbound.objbox.FoodClassInfo
import com.sw.inbound.objbox.ObjectBox
import com.sw.inbound.utils.ext.toast
import com.sw.inbound.utils.ZipDownloadUtils
import com.sw.inbound.utils.ZipExtractUtils
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
/**
* 趣味页面 Activity
* 用于测试 Zip 文件下载、解压和数据库操作
*/
@AndroidEntryPoint
class FunActivity : BaseActivity() {
private lateinit var binding: ActivityFunBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFunBinding.inflate(layoutInflater)
setContentView(binding.root)
initViews()
}
/**
* 初始化各控件的点击事件
*/
private fun initViews() {
// 返回按钮点击事件
binding.ivBack.setOnClickListener {
finish()
}
// 下载按钮
binding.btnDownload.setOnClickListener {
downloadZipFile()
}
// 解压按钮
binding.btnExtract.setOnClickListener {
extractZipFile()
}
// 读取并保存按钮
binding.btnLoadAndSave.setOnClickListener {
loadAndSaveToDatabase()
}
}
/**
* 下载 Zip 文件
*/
private fun downloadZipFile() {
lifecycleScope.launch {
try {
updateProgress("开始下载...")
binding.btnDownload.isEnabled = false
val zipFile = File(cacheDir, "model_resources.zip")
// 假的下载地址
val downloadUrl = "https://dev.yixiong-tech.com:8081/common/files/20260313/embedding_model_resources.zip"
withContext(Dispatchers.IO) {
ZipDownloadUtils.downloadZip(
downloadUrl = downloadUrl,
targetFile = zipFile,
listener = object : ZipDownloadUtils.DownloadProgressListener {
override fun onProgress(currentSize: Long, totalSize: Long, progress: Int) {
updateProgress("下载中... $progress%")
}
override fun onSuccess(file: File) {
updateProgress("下载完成!文件大小: ${formatFileSize(file.length())}")
Timber.d("Zip 文件下载完成: ${file.absolutePath}")
}
override fun onFailure(exception: Exception) {
updateProgress("下载失败: ${exception.message}")
Timber.e(exception, "下载失败")
this@FunActivity.toast("下载失败: ${exception.message}")
}
}
)
}
} catch (e: Exception) {
Timber.e(e, "下载异常")
updateProgress("异常: ${e.message}")
this@FunActivity.toast("异常: ${e.message}")
} finally {
binding.btnDownload.isEnabled = true
}
}
}
/**
* 解压 Zip 文件
*/
private fun extractZipFile() {
lifecycleScope.launch {
try {
updateProgress("开始解压...")
binding.btnExtract.isEnabled = false
val zipFile = File(cacheDir, "model_resources.zip")
val targetDir = File(cacheDir, "model_resources")
if (!zipFile.exists()) {
updateProgress("Zip 文件不存在,请先下载")
this@FunActivity.toast("Zip 文件不存在,请先下载")
return@launch
}
withContext(Dispatchers.IO) {
ZipExtractUtils.extractZip(
zipFile = zipFile,
targetDir = targetDir,
listener = object : ZipExtractUtils.ExtractProgressListener {
override fun onProgress(
currentFile: String,
currentIndex: Int,
totalFiles: Int,
progress: Int
) {
updateProgress("解压中... $progress% ($currentIndex/$totalFiles)")
}
override fun onSuccess(targetDir: File) {
updateProgress("解压完成!目录: ${targetDir.absolutePath}")
Timber.d("Zip 文件解压完成: ${targetDir.absolutePath}")
}
override fun onFailure(exception: Exception) {
updateProgress("解压失败: ${exception.message}")
Timber.e(exception, "解压失败")
this@FunActivity.toast("解压失败: ${exception.message}")
}
}
)
}
} catch (e: Exception) {
Timber.e(e, "解压异常")
updateProgress("异常: ${e.message}")
this@FunActivity.toast("异常: ${e.message}")
} finally {
binding.btnExtract.isEnabled = true
}
}
}
/**
* 读取解压后的文件并保存到 ObjectBox 数据库
*/
private fun loadAndSaveToDatabase() {
lifecycleScope.launch {
try {
updateProgress("开始读取数据...")
binding.btnLoadAndSave.isEnabled = false
val resourcesDir = File(cacheDir, "model_resources")
if (!resourcesDir.exists()) {
updateProgress("资源目录不存在,请先解压")
this@FunActivity.toast("资源目录不存在,请先解压")
return@launch
}
withContext(Dispatchers.IO) {
// 清除已有数据
updateProgress("清除已有数据...")
clearFoodData()
// 读取 JSON 文件
updateProgress("读取 JSON 文件...")
val embeddingsJson = readFileContent(File(resourcesDir, "data/embeddings.json"))
val labelsJson = readFileContent(File(resourcesDir, "data/labels.json"))
val classInfoJson = readFileContent(File(resourcesDir, "data/class_info.json"))
if (embeddingsJson.isEmpty() || labelsJson.isEmpty() || classInfoJson.isEmpty()) {
updateProgress("JSON 文件读取失败")
this@FunActivity.toast("JSON 文件读取失败")
return@withContext
}
// 解析 JSON 数据
updateProgress("解析 JSON 数据...")
val embeddingsList: List<List<Float>> =
Gson().fromJson(embeddingsJson, object : TypeToken<List<List<Float>>>() {}.type)
val labelsList: IntArray = Gson().fromJson(labelsJson, IntArray::class.java)
val classInfo: FoodClassInfo =
Gson().fromJson(classInfoJson, FoodClassInfo::class.java)
// 构建 Food 对象列表
updateProgress("构建数据对象...")
val foodMap = classInfo.idx_to_class
val foodList = mutableListOf<Food>()
val size = embeddingsList.size
Timber.d("开始构建 Food 对象,总数: $size")
embeddingsList.forEachIndexed { index, floatList ->
val classIdx = labelsList[index]
val foodName = foodMap["$classIdx"]
val array = floatList.toFloatArray()
val food = Food(name = foodName, foodVector = array, foodIdx = -1)
foodList.add(food)
// 每 100 条更新一次进度
if ((index + 1) % 100 == 0) {
val progress = ((index + 1) * 100) / size
updateProgress("构建数据中... $progress% (${index + 1}/$size)")
}
}
// 保存到数据库
updateProgress("保存到数据库...")
ObjectBox.putAll(foodList)
updateProgress("完成!已保存 ${foodList.size} 条数据到数据库")
Timber.d("数据保存完成,总数: ${foodList.size}")
}
} catch (e: Exception) {
Timber.e(e, "读取和保存异常")
updateProgress("异常: ${e.message}")
this@FunActivity.toast("异常: ${e.message}")
} finally {
binding.btnLoadAndSave.isEnabled = true
}
}
}
/**
* 清除 Food 表中的所有数据
*/
private suspend fun clearFoodData() {
try {
ObjectBox.safeDbOp {
val box = ObjectBox.getBox<Food>()
box?.removeAll()
Timber.d("Food 表数据已清除")
}
} catch (e: Exception) {
Timber.e(e, "清除数据失败")
}
}
/**
* 读取文件内容
*/
private fun readFileContent(file: File): String {
return try {
if (!file.exists()) {
Timber.w("文件不存在: ${file.absolutePath}")
""
} else {
file.readText()
}
} catch (e: Exception) {
Timber.e(e, "读取文件失败: ${file.absolutePath}")
""
}
}
/**
* 更新进度显示
*/
private fun updateProgress(message: String) {
runOnUiThread {
binding.tvProgress.text = message
Timber.d("进度: $message")
}
}
/**
* 格式化文件大小
*/
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))
}
}
}