feat: cache manager and webapp manifest repository

This commit is contained in:
2026-08-16 16:23:48 +08:00
parent 0153ec38ae
commit f3405e003f
4 changed files with 138 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
package top.yeij.hearth.cache
import java.io.File
// 文件缓存:以 key 为文件名,内容以 UTF-8 纯文本落盘
// File cache: uses key as filename, content stored as UTF-8 plain text
class CacheManager(private val baseDir: File) {
init { baseDir.mkdirs() }
fun save(key: String, content: String) {
File(baseDir, key).writeText(content)
}
fun load(key: String): String? {
val f = File(baseDir, key)
return if (f.exists()) f.readText() else null
}
}
@@ -0,0 +1,51 @@
package top.yeij.hearth.webapp
import com.google.gson.Gson
import top.yeij.hearth.cache.CacheManager
// Web 应用清单条目
// Web app manifest entry
data class WebApp(val id: String, val name: String, val icon: String, val url: String)
// 网络抽象接口:Task 10 的 CardRepository 也会复用
// HTTP abstraction reused by Task 10 CardRepository
interface HttpClient {
fun get(url: String): String?
}
// 清单拉取仓库:拉取成功缓存 body 并解析;失败回退缓存;无缓存返回空列表
// Manifest repository: on success cache + parse; on failure fall back to cache; no cache -> empty list
class WebAppRepository(
private val http: HttpClient,
private val cache: CacheManager,
private val manifestUrl: String,
) {
private val gson = Gson()
fun fetchManifest(): List<WebApp> {
val body = http.get(manifestUrl)
if (body != null) {
cache.save(KEY, body)
return parse(body)
}
val cached = cache.load(KEY) ?: return emptyList()
return parse(cached)
}
private fun parse(body: String): List<WebApp> {
val root = gson.fromJson(body, Map::class.java)
val apps = root["apps"] as? List<*> ?: return emptyList()
return apps.mapNotNull { m ->
val map = m as? Map<*, *> ?: return@mapNotNull null
val id = map["id"] as? String ?: return@mapNotNull null
val name = map["name"] as? String ?: return@mapNotNull null
val icon = map["icon"] as? String ?: return@mapNotNull null
val url = map["url"] as? String ?: return@mapNotNull null
WebApp(id, name, icon, url)
}
}
companion object {
private const val KEY = "webapp-manifest"
}
}