fix: BLE 로그 Downloads 폴더에 저장 (파일 탐색기 접근 가능)

- exportToDownloads(): Downloads/VesiScan_BLE_날짜.log 로 저장
- 실패 시 fallback으로 내부 저장소에 저장
- Toast로 저장 경로 안내

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 12:22:07 +09:00
parent 6f4488f1ae
commit 506248655d
2 changed files with 43 additions and 23 deletions
@@ -207,32 +207,47 @@ class BleDebugLogger private constructor(private val context: Context) {
// ── File Export ── // ── File Export ──
private fun writeLogContent(writer: java.io.BufferedWriter) {
writer.write("=== VesiScan BLE Debug Log ===\n")
writer.write("Device: $deviceName\n")
writer.write("Session: ${dateFmt.format(Date(sessionStart))}\n")
writer.write("Duration: ${sessionDurationSec()}s\n")
writer.write("TX: $txCount RX: $rxCount Errors: $errorCount Timeouts: $packetTimeouts\n")
if (rssiHistory.isNotEmpty()) {
writer.write("RSSI: avg=${rssiHistory.map { it.second }.average().toInt()} ")
writer.write("min=${rssiHistory.minOf { it.second }} max=${rssiHistory.maxOf { it.second }}\n")
}
writer.write("=".repeat(60) + "\n\n")
for (entry in entries) {
writer.write(entry.toLogLine() + "\n")
}
}
fun exportToFile(): File { fun exportToFile(): File {
val fileName = "ble_debug_${dateFmt.format(Date())}.log" val fileName = "ble_debug_${dateFmt.format(Date())}.log"
val file = File(logDir, fileName) val file = File(logDir, fileName)
file.bufferedWriter().use { writeLogContent(it) }
file.bufferedWriter().use { writer ->
writer.write("=== VesiScan BLE Debug Log ===\n")
writer.write("Device: $deviceName\n")
writer.write("Session: ${dateFmt.format(Date(sessionStart))}\n")
writer.write("Duration: ${sessionDurationSec()}s\n")
writer.write("TX: $txCount RX: $rxCount Errors: $errorCount Timeouts: $packetTimeouts\n")
if (rssiHistory.isNotEmpty()) {
writer.write("RSSI: avg=${rssiHistory.map { it.second }.average().toInt()} ")
writer.write("min=${rssiHistory.minOf { it.second }} max=${rssiHistory.maxOf { it.second }}\n")
}
writer.write("=".repeat(60) + "\n\n")
for (entry in entries) {
writer.write(entry.toLogLine() + "\n")
}
}
return file return file
} }
/** Downloads 폴더에 저장 (파일 탐색기에서 바로 접근 가능) */
fun exportToDownloads(): File? {
return try {
val downloadsDir = android.os.Environment.getExternalStoragePublicDirectory(
android.os.Environment.DIRECTORY_DOWNLOADS
)
val fileName = "VesiScan_BLE_${dateFmt.format(Date())}.log"
val file = File(downloadsDir, fileName)
file.bufferedWriter().use { writeLogContent(it) }
file
} catch (e: Exception) {
null
}
}
fun exportToJsonFile(): File { fun exportToJsonFile(): File {
val fileName = "ble_debug_${dateFmt.format(Date())}.jsonl" val fileName = "ble_debug_${dateFmt.format(Date())}.jsonl"
val file = File(logDir, fileName) val file = File(logDir, fileName)
@@ -1115,11 +1115,16 @@ private fun BleDebugPanel(bleManager: BleManager) {
Icon(Icons.Default.ContentCopy, "Copy", tint = Color.Gray, modifier = Modifier.size(16.dp)) Icon(Icons.Default.ContentCopy, "Copy", tint = Color.Gray, modifier = Modifier.size(16.dp))
} }
// Export file button // Export to Downloads
IconButton( IconButton(
onClick = { onClick = {
val file = logger.exportToFile() val file = logger.exportToDownloads()
android.widget.Toast.makeText(context, "Saved: ${file.name}", android.widget.Toast.LENGTH_SHORT).show() if (file != null) {
android.widget.Toast.makeText(context, "Downloads/${file.name} 저장 완료", android.widget.Toast.LENGTH_LONG).show()
} else {
logger.exportToFile()
android.widget.Toast.makeText(context, "내부 저장소에 저장됨", android.widget.Toast.LENGTH_SHORT).show()
}
}, },
modifier = Modifier.size(32.dp) modifier = Modifier.size(32.dp)
) { ) {