diff --git a/app/src/main/assets/h5/js/pages/settings.js b/app/src/main/assets/h5/js/pages/settings.js
index 6c23aae..615ecca 100644
--- a/app/src/main/assets/h5/js/pages/settings.js
+++ b/app/src/main/assets/h5/js/pages/settings.js
@@ -42,6 +42,10 @@ window.loadSettings = function () {
›
关于
+
+ 导出日志
+ ›
+
版本 0.1.0›
`;
setupTheme(page);
@@ -51,6 +55,7 @@ window.loadSettings = function () {
setupBrightness(page);
setupServerUrl(page);
setupCheckUpdate(page);
+ setupExportLog(page);
};
// 判断当前是否深色主题(与 mask.js 逻辑一致)
@@ -238,6 +243,21 @@ function setupCheckUpdate(page) {
};
}
+// 导出日志:点击导出到 Download,显示结果
+// Export log: tap to export to Download, show the result
+function setupExportLog(page) {
+ const item = page.querySelector('#export-log-item');
+ const state = page.querySelector('#export-log-state');
+ item.onclick = () => {
+ state.textContent = '导出中…';
+ bridge.call('exportLog').then(result => {
+ state.textContent = result + ' ›';
+ }).catch(() => {
+ state.textContent = '失败 ›';
+ });
+ };
+}
+
// 通用滑块拖动:touch 拖动实时回调(保留 click 兼容桌面调试)
// Generic draggable slider: touch-drag with live callback (click kept for desktop)
function makeSliderDraggable(slider, onRatio) {
diff --git a/app/src/main/java/top/yeij/hearth/MainActivity.kt b/app/src/main/java/top/yeij/hearth/MainActivity.kt
index c09e573..3420136 100644
--- a/app/src/main/java/top/yeij/hearth/MainActivity.kt
+++ b/app/src/main/java/top/yeij/hearth/MainActivity.kt
@@ -2,6 +2,7 @@ package top.yeij.hearth
import android.app.Activity
import android.app.WallpaperManager
+import android.content.ContentValues
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Bitmap
@@ -10,6 +11,7 @@ import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Bundle
+import android.provider.MediaStore
import android.provider.Settings
import android.text.TextUtils
import android.util.Base64
@@ -215,6 +217,7 @@ class MainActivity : Activity() {
onShowTabPanel = { showNativeTabPanel() },
storage = webAppStorage,
onImportOfflinePackage = { id -> startImportOfflinePackage(id) },
+ onExportLog = { exportLog() },
)
root = FrameLayout(this)
setContentView(root)
@@ -436,6 +439,34 @@ class MainActivity : Activity() {
// dp to px
private fun dp(v: Int): Int = (v * resources.displayMetrics.density).toInt()
+ // 导出日志到 Download 目录(读 logcat 本进程 + 过滤 Hearth 相关行)
+ // Export the log to the Download directory (read logcat, filter Hearth lines)
+ private fun exportLog(): String {
+ return try {
+ val process = Runtime.getRuntime().exec(arrayOf("logcat", "-d"))
+ val log = process.inputStream.bufferedReader().readText()
+ val filtered = log.lines()
+ .filter { it.contains("Hearth") }
+ .joinToString("\n")
+ val name = "hearth-log-" + System.currentTimeMillis() + ".txt"
+ val values = ContentValues().apply {
+ put(MediaStore.Downloads.DISPLAY_NAME, name)
+ put(MediaStore.Downloads.MIME_TYPE, "text/plain")
+ put(MediaStore.Downloads.RELATIVE_PATH, "Download/")
+ }
+ val uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
+ ?: return "导出失败:无法创建文件"
+ contentResolver.openOutputStream(uri)?.use { out ->
+ out.write(filtered.toByteArray())
+ } ?: return "导出失败:无法写入"
+ Log.d(TAG, "exportLog: $name (${filtered.length} chars)")
+ "已导出:Download/$name"
+ } catch (e: Exception) {
+ Log.e(TAG, "exportLog failed: ${e.message}")
+ "导出失败:${e.message}"
+ }
+ }
+
// 触发 SAF 选择 zip 导入离线包
// Trigger SAF to pick a zip for offline-package import
private fun startImportOfflinePackage(id: String) {
diff --git a/app/src/main/java/top/yeij/hearth/webapp/WebAppRepository.kt b/app/src/main/java/top/yeij/hearth/webapp/WebAppRepository.kt
index 0419129..f0fb72e 100644
--- a/app/src/main/java/top/yeij/hearth/webapp/WebAppRepository.kt
+++ b/app/src/main/java/top/yeij/hearth/webapp/WebAppRepository.kt
@@ -41,6 +41,7 @@ class WebAppRepository(
fun fetchManifest(): List {
val body = http.get(manifestUrl)
+ Log.d(TAG, "fetchManifest: url=$manifestUrl bodyNull=${body == null}")
if (body != null) {
// 先解析、成功才缓存,避免畸形 body 或空结果被永久缓存
// Parse first, cache only on success (never cache malformed/empty bodies)
diff --git a/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt b/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt
index 76c655b..b758a1d 100644
--- a/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt
+++ b/app/src/main/java/top/yeij/hearth/webview/JsBridge.kt
@@ -81,6 +81,9 @@ class JsBridge(
// 手动导入离线包回调:由 MainActivity 实现(SAF 选择 zip 后导入)
// Manual offline-package import callback implemented by MainActivity (SAF pick + import)
private val onImportOfflinePackage: ((String) -> Unit)? = null,
+ // 导出日志回调:由 MainActivity 实现,返回导出结果描述(文件名/错误)
+ // Export-log callback implemented by MainActivity, returns a result description
+ private val onExportLog: (() -> String)? = null,
) {
private val gson = com.google.gson.Gson()
@@ -185,6 +188,14 @@ class JsBridge(
@android.webkit.JavascriptInterface
fun hasLocalPackage(id: String): Boolean = storage?.hasLocalPackage(id) ?: false
+ // 导出日志到 Download 目录,返回结果描述(文件名或错误)
+ // Export the log to the Download directory, return a result description
+ @android.webkit.JavascriptInterface
+ fun exportLog(): String {
+ Log.d("HearthBridge", "exportLog called")
+ return onExportLog?.invoke() ?: "日志导出未实现"
+ }
+
// 本地离线包版本号(无则空字符串)
// Local offline-package version (empty string when absent)
@android.webkit.JavascriptInterface