179 lines
6.6 KiB
Kotlin
179 lines
6.6 KiB
Kotlin
package com.example.shiftalarm
|
|
|
|
import android.content.Context
|
|
import android.net.Uri
|
|
import org.json.JSONArray
|
|
import org.json.JSONObject
|
|
import java.io.InputStreamReader
|
|
import java.io.OutputStreamWriter
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.withContext
|
|
|
|
/**
|
|
* Handles data backup and restoration (Database + SharedPreferences).
|
|
* Format: JSON
|
|
*/
|
|
object BackupManager {
|
|
|
|
suspend fun backupData(context: Context, uri: Uri, dao: ShiftDao) = withContext(Dispatchers.IO) {
|
|
val overrides = dao.getAllOverrides()
|
|
val memos = dao.getAllMemos()
|
|
|
|
val json = JSONObject()
|
|
|
|
// 1. Backup Overrides
|
|
val overrideArray = JSONArray()
|
|
overrides.forEach {
|
|
overrideArray.put(JSONObject().apply {
|
|
put("date", it.date)
|
|
put("shift", it.shift)
|
|
put("team", it.team)
|
|
put("factory", it.factory)
|
|
})
|
|
}
|
|
json.put("overrides", overrideArray)
|
|
|
|
// 1.5 Backup Custom Alarms
|
|
val customAlarms = dao.getAllCustomAlarms()
|
|
val customAlarmArray = JSONArray()
|
|
customAlarms.forEach {
|
|
customAlarmArray.put(JSONObject().apply {
|
|
put("time", it.time)
|
|
put("shiftType", it.shiftType)
|
|
put("isEnabled", it.isEnabled)
|
|
put("soundUri", it.soundUri)
|
|
put("snoozeInterval", it.snoozeInterval)
|
|
put("snoozeRepeat", it.snoozeRepeat)
|
|
})
|
|
}
|
|
json.put("custom_alarms_v2", customAlarmArray)
|
|
|
|
// 2. Backup Memos
|
|
val memoArray = JSONArray()
|
|
memos.forEach {
|
|
memoArray.put(JSONObject().apply {
|
|
put("date", it.date)
|
|
put("content", it.content)
|
|
})
|
|
}
|
|
json.put("memos", memoArray)
|
|
|
|
// 3. Backup Settings (SharedPreferences)
|
|
val prefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
|
val settings = JSONObject()
|
|
prefs.all.forEach { (key, value) ->
|
|
if (value is String) settings.put(key, value)
|
|
else if (value is Boolean) settings.put(key, value)
|
|
else if (value is Int) settings.put(key, value)
|
|
else if (value is Float) settings.put(key, value.toDouble())
|
|
else if (value is Long) settings.put(key, value)
|
|
else if (value is Double) settings.put(key, value)
|
|
}
|
|
json.put("settings", settings)
|
|
|
|
json.put("magic", "SHIFTRING_BACKUP_V3")
|
|
json.put("timestamp", System.currentTimeMillis())
|
|
|
|
val finalString = json.toString()
|
|
val encodedBytes = android.util.Base64.encode(finalString.toByteArray(), android.util.Base64.DEFAULT)
|
|
|
|
context.contentResolver.openOutputStream(uri)?.use { os ->
|
|
os.write(encodedBytes)
|
|
}
|
|
}
|
|
|
|
suspend fun restoreData(context: Context, uri: Uri, dao: ShiftDao) = withContext(Dispatchers.IO) {
|
|
val bytes = context.contentResolver.openInputStream(uri)?.use {
|
|
it.readBytes()
|
|
} ?: throw Exception("Failed to read file")
|
|
|
|
var content = ""
|
|
try {
|
|
// Try Base64 first (V3)
|
|
val decodedBytes = android.util.Base64.decode(bytes, android.util.Base64.DEFAULT)
|
|
content = String(decodedBytes)
|
|
} catch (e: Exception) {
|
|
// Fallback to plain text (V1/V2)
|
|
content = String(bytes)
|
|
}
|
|
|
|
val json = JSONObject(content)
|
|
|
|
val magic = json.optString("magic", "")
|
|
if (magic != "SHIFTRING_BACKUP_V1" && magic != "SHIFTRING_BACKUP_V2" && magic != "SHIFTRING_BACKUP_V3") {
|
|
throw Exception("올바르지 않은 백업 파일 형식입니다.")
|
|
}
|
|
|
|
// 1. Restore Settings FIRST
|
|
if (json.has("settings")) {
|
|
val settings = json.getJSONObject("settings")
|
|
val prefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE).edit()
|
|
prefs.clear()
|
|
val keys = settings.keys()
|
|
while(keys.hasNext()) {
|
|
val key = keys.next()
|
|
if (settings.isNull(key)) continue
|
|
|
|
val value = settings.get(key)
|
|
when(value) {
|
|
is Boolean -> prefs.putBoolean(key, value)
|
|
is Int -> prefs.putInt(key, value)
|
|
is String -> prefs.putString(key, value)
|
|
is Double -> prefs.putFloat(key, value.toFloat())
|
|
is Long -> prefs.putLong(key, value)
|
|
}
|
|
}
|
|
prefs.apply()
|
|
}
|
|
|
|
val restoredPrefs = context.getSharedPreferences("ShiftAlarmPrefs", Context.MODE_PRIVATE)
|
|
val fallbackFactory = restoredPrefs.getString("selected_factory", "Jeonju") ?: "Jeonju"
|
|
val fallbackTeam = restoredPrefs.getString("selected_team", "A") ?: "A"
|
|
|
|
// 2. Restore Overrides
|
|
if (json.has("overrides")) {
|
|
dao.clearOverrides()
|
|
val arr = json.getJSONArray("overrides")
|
|
for (i in 0 until arr.length()) {
|
|
val obj = arr.getJSONObject(i)
|
|
dao.insertOverride(ShiftOverride(
|
|
factory = obj.optString("factory", fallbackFactory),
|
|
team = obj.optString("team", fallbackTeam),
|
|
date = obj.getString("date"),
|
|
shift = obj.getString("shift")
|
|
))
|
|
}
|
|
}
|
|
|
|
// 2.5 Restore Custom Alarms
|
|
if (json.has("custom_alarms_v2")) {
|
|
dao.clearCustomAlarms()
|
|
val arr = json.getJSONArray("custom_alarms_v2")
|
|
for (i in 0 until arr.length()) {
|
|
val obj = arr.getJSONObject(i)
|
|
dao.insertCustomAlarm(CustomAlarm(
|
|
time = obj.getString("time"),
|
|
shiftType = obj.getString("shiftType"),
|
|
isEnabled = obj.optBoolean("isEnabled", true),
|
|
soundUri = obj.optString("soundUri", null),
|
|
snoozeInterval = obj.optInt("snoozeInterval", 5),
|
|
snoozeRepeat = obj.optInt("snoozeRepeat", 3)
|
|
))
|
|
}
|
|
}
|
|
|
|
// 3. Restore Memos
|
|
if (json.has("memos")) {
|
|
dao.clearMemos()
|
|
val arr = json.getJSONArray("memos")
|
|
for (i in 0 until arr.length()) {
|
|
val obj = arr.getJSONObject(i)
|
|
dao.insertMemo(DailyMemo(
|
|
obj.getString("date"),
|
|
obj.getString("content")
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|