From 0a231f654249db5c9a23e9b9e74f88f83b336bbf Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Tue, 28 Jul 2026 09:21:21 +0300 Subject: [PATCH] Use JSON format when backing up calls and messages --- .../com/tutpro/baresip/BaresipService.kt | 4 +- .../kotlin/com/tutpro/baresip/CallHistory.kt | 46 +++++++++++++----- .../main/kotlin/com/tutpro/baresip/Message.kt | 48 ++++++++++++++----- .../main/kotlin/com/tutpro/baresip/Utils.kt | 16 +++++++ 4 files changed, 89 insertions(+), 25 deletions(-) diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 562db96c..088050db 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -578,6 +578,7 @@ class BaresipService: Service() { CallHistoryNew.restore() Blocked.restore() BlockRule.restore() + Message.restore() val recordings = File(filesDir, "recordings") @@ -586,6 +587,7 @@ class BaresipService: Service() { Log.d(TAG, "Clearing recordings") CallHistoryNew.clearRecordings() CallHistoryNew.save() + Message.save() if (recordings.exists()) recordings.deleteRecursively() restored.delete() @@ -593,8 +595,6 @@ class BaresipService: Service() { File(filesDir, "recordings").mkdir() File(filesDir, "tmp").mkdir() - - Message.restore() }.start() hotSpotAddresses = Utils.hotSpotAddresses() diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt b/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt index 1afb3e1c..db6a469e 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallHistory.kt @@ -1,18 +1,22 @@ package com.tutpro.baresip +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.IOException import java.io.ObjectInputStream import java.io.ObjectOutputStream -import java.io.Serializable import java.util.GregorianCalendar -class CallHistoryNew(val aor: String, val peerUri: String, val direction: String) : Serializable { +@Serializable +class CallHistoryNew(val aor: String, val peerUri: String, val direction: String) : java.io.Serializable { // Set to time when call is established (if ever) or stopTime if call was completed elsewhere + @Serializable(with = Utils.GregorianCalendarSerializer::class) var startTime: GregorianCalendar? = null + @Serializable(with = Utils.GregorianCalendarSerializer::class) var stopTime = GregorianCalendar() // Set to time when call is closed var rejected = false var recording = arrayOf("", "") // Encoder and decoder recording files, merged file is in [0] @@ -78,30 +82,48 @@ class CallHistoryNew(val aor: String, val peerUri: String, val direction: String Log.d(TAG, "Saving history of ${historyCopy.size} calls") val file = File(BaresipService.filesPath + "/call_history") try { - val fos = FileOutputStream(file) - val oos = ObjectOutputStream(fos) - oos.writeObject(historyCopy) - oos.close() - fos.close() - } catch (e: IOException) { - Log.e(TAG, "OutputStream exception: $e") - e.printStackTrace() + val jsonString = Json.encodeToString(historyCopy) + file.writeText(jsonString) + } catch (e: Exception) { + Log.e(TAG, "Serialization exception: $e") + try { + val fos = FileOutputStream(file) + val oos = ObjectOutputStream(fos) + oos.writeObject(historyCopy) + oos.close() + fos.close() + } catch (e2: IOException) { + Log.e(TAG, "OutputStream exception: $e2") + } } } fun restore() { val file = File(BaresipService.filesPath + "/call_history") if (file.exists()) { + val content = file.readText() + if (content.startsWith("[")) { + try { + val restoredHistory = Json.decodeFromString>(content) + BaresipService.callHistory = ArrayList(restoredHistory) + Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls from JSON") + return + } catch (e: Exception) { + Log.d(TAG, "JSON restore failed, trying Java serialization: $e") + } + } try { val fis = FileInputStream(file) val ois = ObjectInputStream(fis) @Suppress("UNCHECKED_CAST") val restoredHistory = ois.readObject() as? List - if (restoredHistory != null) + if (restoredHistory != null) { BaresipService.callHistory = ArrayList(restoredHistory) + Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls from Java") + save() + } ois.close() fis.close() - Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls") } catch (e: Exception) { Log.e(TAG, "InputStream exception: - $e") } diff --git a/app/src/main/kotlin/com/tutpro/baresip/Message.kt b/app/src/main/kotlin/com/tutpro/baresip/Message.kt index efe72398..bacbca6b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Message.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Message.kt @@ -1,10 +1,13 @@ package com.tutpro.baresip +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json import java.io.* +@Serializable class Message(val aor: String, val peerUri: String, val message: String, val timeStamp: Long, var direction: Int, var responseCode: Int, var responseReason: String, - var new: Boolean): Serializable { + var new: Boolean): java.io.Serializable { fun add() { val updatedMessages = synchronized(BaresipService.messages) { @@ -42,6 +45,9 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim companion object { + @Suppress("unused") + const val serialVersionUID: Long = 434313168853691766L + const val MESSAGE_HISTORY_SIZE = 100 fun messages(): List { @@ -133,30 +139,50 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim } val file = File(BaresipService.filesPath, "messages") try { - val fos = FileOutputStream(file) - val oos = ObjectOutputStream(fos) - oos.writeObject(messagesCopy) - oos.close() - fos.close() - Log.d(TAG, "Saved ${messagesCopy.size} messages") - } catch (e: IOException) { - Log.e(TAG, "OutputStream exception", e) + val jsonString = Json.encodeToString(messagesCopy) + file.writeText(jsonString) + Log.d(TAG, "Saved ${messagesCopy.size} messages in JSON") + } catch (e: Exception) { + Log.e(TAG, "Serialization exception", e) + try { + val fos = FileOutputStream(file) + val oos = ObjectOutputStream(fos) + oos.writeObject(messagesCopy) + oos.close() + fos.close() + Log.d(TAG, "Saved ${messagesCopy.size} messages in Java") + } catch (e2: IOException) { + Log.e(TAG, "OutputStream exception", e2) + } } } fun restore() { val file = File(BaresipService.filesPath, "messages") if (file.exists()) { + val content = file.readText() + if (content.startsWith("[")) { + try { + val restoredMessages = Json.decodeFromString>(content) + BaresipService.messages = restoredMessages + Log.d(TAG, "Restored ${BaresipService.messages.size} messages from JSON") + return + } catch (e: Exception) { + Log.d(TAG, "JSON restore failed, trying Java serialization: $e") + } + } try { val fis = FileInputStream(file) val ois = ObjectInputStream(fis) @Suppress("UNCHECKED_CAST") val restoredMessages = ois.readObject() as? List - if (restoredMessages != null) + if (restoredMessages != null) { BaresipService.messages = restoredMessages + Log.d(TAG, "Restored ${BaresipService.messages.size} messages from Java") + save() + } ois.close() fis.close() - Log.d(TAG, "Restored ${BaresipService.messages.size} messages") } catch (e: Exception) { Log.e(TAG, "InputStream exception: - $e") } diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt index 25b67424..880cdc89 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -55,6 +55,12 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.ProcessLifecycleOwner import androidx.navigation.NavController import com.tutpro.baresip.Call.Companion.inCall +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import java.io.BufferedInputStream import java.io.BufferedOutputStream import java.io.File @@ -1579,4 +1585,14 @@ object Utils { if (p1.routes != p2.routes) return false return true } + + object GregorianCalendarSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("GregorianCalendar", PrimitiveKind.LONG) + override fun serialize(encoder: Encoder, value: GregorianCalendar) = encoder.encodeLong(value.timeInMillis) + override fun deserialize(decoder: Decoder): GregorianCalendar { + val calendar = GregorianCalendar() + calendar.timeInMillis = decoder.decodeLong() + return calendar + } + } }