138 lines
5.5 KiB
Kotlin
138 lines
5.5 KiB
Kotlin
package com.example.shiftalarm
|
|
|
|
import android.os.Bundle
|
|
import android.view.MenuItem
|
|
import android.widget.Toast
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.activity.enableEdgeToEdge
|
|
import androidx.core.view.ViewCompat
|
|
import androidx.core.view.WindowInsetsCompat
|
|
import com.example.shiftalarm.databinding.ActivityNoticeBinding
|
|
import java.io.BufferedReader
|
|
import java.io.InputStreamReader
|
|
import java.net.HttpURLConnection
|
|
import java.net.URL
|
|
|
|
class NoticeActivity : AppCompatActivity() {
|
|
|
|
private lateinit var binding: ActivityNoticeBinding
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
enableEdgeToEdge()
|
|
super.onCreate(savedInstanceState)
|
|
binding = ActivityNoticeBinding.inflate(layoutInflater)
|
|
setContentView(binding.root)
|
|
|
|
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
|
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
|
insets
|
|
}
|
|
|
|
supportActionBar?.hide()
|
|
|
|
binding.btnCloseNotice.setOnClickListener {
|
|
finish()
|
|
}
|
|
|
|
binding.noticeRecyclerView.layoutManager = LinearLayoutManager(this)
|
|
fetchChangelog()
|
|
}
|
|
|
|
private fun fetchChangelog() {
|
|
// GitHub Raw URL with cache busting
|
|
val baseUrl = "https://raw.githubusercontent.com/sanjeok77-tech/dakjaba-releases/main/CHANGELOG.md"
|
|
val urlString = "$baseUrl?t=${System.currentTimeMillis()}"
|
|
|
|
Thread {
|
|
try {
|
|
val url = URL(urlString)
|
|
val connection = url.openConnection() as HttpURLConnection
|
|
connection.connectTimeout = 5000
|
|
connection.readTimeout = 5000
|
|
connection.requestMethod = "GET"
|
|
connection.useCaches = false
|
|
|
|
if (connection.responseCode == 200) {
|
|
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
|
val content = reader.use { it.readText() }
|
|
|
|
runOnUiThread {
|
|
val notices = parseChangelog(content)
|
|
binding.noticeRecyclerView.adapter = NoticeAdapter(notices)
|
|
}
|
|
} else {
|
|
throw Exception("Server returned ${connection.responseCode}")
|
|
}
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
runOnUiThread {
|
|
// Fallback to local asset
|
|
loadLocalChangelog()
|
|
}
|
|
}
|
|
}.start()
|
|
}
|
|
|
|
private fun loadLocalChangelog() {
|
|
try {
|
|
val content = assets.open("CHANGELOG.md").bufferedReader().use { it.readText() }
|
|
val notices = parseChangelog(content)
|
|
binding.noticeRecyclerView.adapter = NoticeAdapter(notices)
|
|
} catch (e: Exception) {
|
|
val empty = listOf(NoticeItem("데이터 로드 실패", "", "변경사항을 불러올 수 없습니다."))
|
|
binding.noticeRecyclerView.adapter = NoticeAdapter(empty)
|
|
}
|
|
}
|
|
|
|
private fun parseChangelog(content: String): List<NoticeItem> {
|
|
val notices = mutableListOf<NoticeItem>()
|
|
val lines = content.lines()
|
|
|
|
var currentVersion = ""
|
|
var currentDate = ""
|
|
var currentBody = StringBuilder()
|
|
|
|
for (line in lines) {
|
|
val trimmed = line.trim()
|
|
// Skip empty lines or horizontal rules (any amount of dashes)
|
|
if (trimmed.isEmpty() || trimmed.matches(Regex("-{2,}"))) continue
|
|
|
|
// Handle version headers like "## v0.7.3" or "## [0.7.3]"
|
|
if (trimmed.startsWith("## v") || trimmed.startsWith("## [")) {
|
|
// Save previous version if exists
|
|
if (currentVersion.isNotEmpty() && currentBody.isNotBlank()) {
|
|
notices.add(NoticeItem("v$currentVersion 업데이트 정보", currentDate, currentBody.toString().trim()))
|
|
}
|
|
|
|
// Parse new version (matches v0.7.3 or [0.7.3])
|
|
val versionMatch = Regex("v?([\\d.]+)").find(trimmed)
|
|
currentVersion = versionMatch?.groupValues?.getOrNull(1) ?: ""
|
|
|
|
val dateMatch = Regex("(\\d{4}-\\d{2}-\\d{2})").find(trimmed)
|
|
currentDate = dateMatch?.groupValues?.getOrNull(1) ?: ""
|
|
|
|
currentBody = StringBuilder()
|
|
} else if (trimmed.startsWith("- **") || trimmed.startsWith("* **")) {
|
|
// Content line with bold key
|
|
val cleaned = trimmed
|
|
.replace(Regex("^[-*]\\s*\\*\\*(.+?)\\*\\*:?\\s*"), "▸ $1: ")
|
|
.replace("**", "")
|
|
currentBody.appendLine(cleaned)
|
|
} else if (trimmed.startsWith("-") || trimmed.startsWith("*")) {
|
|
// Regular bullet point
|
|
val cleaned = trimmed.replace(Regex("^[-*]\\s*"), "• ")
|
|
if (cleaned.length > 2) currentBody.appendLine(cleaned)
|
|
}
|
|
}
|
|
|
|
// Add last version
|
|
if (currentVersion.isNotEmpty() && currentBody.isNotBlank()) {
|
|
notices.add(NoticeItem("v$currentVersion 업데이트 정보", currentDate, currentBody.toString().trim()))
|
|
}
|
|
|
|
return notices.take(7)
|
|
}
|
|
}
|