Introduced new string mobile_service_not_available and corresponding alert

Fixed restore of call and message history
This commit is contained in:
Juha Heinanen
2026-08-08 11:29:32 +03:00
parent 677b02b020
commit 2eeaea706e
12 changed files with 93 additions and 57 deletions

View File

@ -588,6 +588,8 @@ class BaresipService: Service() {
CallHistoryNew.clearRecordings() CallHistoryNew.clearRecordings()
CallHistoryNew.save() CallHistoryNew.save()
Message.save() Message.save()
Blocked.save()
BlockRule.save()
if (recordings.exists()) if (recordings.exists())
recordings.deleteRecursively() recordings.deleteRecursively()
restored.delete() restored.delete()
@ -595,28 +597,21 @@ class BaresipService: Service() {
File(filesDir, "recordings").mkdir() File(filesDir, "recordings").mkdir()
File(filesDir, "tmp").mkdir() File(filesDir, "tmp").mkdir()
}.start()
hotSpotAddresses = Utils.hotSpotAddresses() hotSpotAddresses = Utils.hotSpotAddresses()
linkAddresses = linkAddresses() linkAddresses = linkAddresses()
var addresses = "" var addresses = ""
for (la in linkAddresses) for (la in linkAddresses)
addresses = "$addresses;${la.key};${la.value}" addresses = "$addresses;${la.key};${la.value}"
Log.i(TAG, "Link addresses: $addresses") Log.i(TAG, "Link addresses: $addresses")
activeNetwork = cm.activeNetwork
Log.i(TAG, "Active network: $activeNetwork")
registerPhoneAccount() val userAgent = Config.variable("user_agent")
val software = if (userAgent != "")
userAgent
else
"baresip v${BuildConfig.VERSION_NAME} " +
"(Android ${VERSION.RELEASE}/${System.getProperty("os.arch") ?: "?"})"
Log.i(TAG, "AEC/AGC/NS available = $aecAvailable/$agcAvailable/$nsAvailable")
val userAgent = Config.variable("user_agent")
val software = if (userAgent != "")
userAgent
else
"baresip v${BuildConfig.VERSION_NAME} " +
"(Android ${VERSION.RELEASE}/${System.getProperty("os.arch") ?: "?"})"
Thread {
baresipStart( baresipStart(
filesPath, filesPath,
addresses.removePrefix(";"), addresses.removePrefix(";"),
@ -627,6 +622,15 @@ class BaresipService: Service() {
isServiceRunning = true isServiceRunning = true
activeNetwork = cm.activeNetwork
Log.i(TAG, "Active network: $activeNetwork")
registerPhoneAccount()
Log.i(TAG, "AEC/AGC/NS available = $aecAvailable/$agcAvailable/$nsAvailable")
isServiceRunning = true
if (linkAddresses.isEmpty()) if (linkAddresses.isEmpty())
toast(getString(R.string.no_network), Toast.LENGTH_LONG) toast(getString(R.string.no_network), Toast.LENGTH_LONG)
@ -1796,11 +1800,15 @@ class BaresipService: Service() {
@Keep @Keep
fun started() { fun started() {
Log.d(TAG, "Received 'started' from baresip") Log.d(TAG, "Received 'started' from baresip")
isNativeReady = true
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
isNativeReady = true
addMobileUserAgent() addMobileUserAgent()
Account.saveAccounts() Account.saveAccounts()
CallHistoryNew.save()
Message.save()
Blocked.save()
BlockRule.save()
if (VERSION.SDK_INT >= 29) if (VERSION.SDK_INT >= 29)
updateMobileStatus() updateMobileStatus()
@ -2400,15 +2408,13 @@ class BaresipService: Service() {
mobileUa.let { ua -> mobileUa.let { ua ->
val isAirplaneModeOn = Utils.isAirplaneModeOn(this) val isAirplaneModeOn = Utils.isAirplaneModeOn(this)
val status = newStatus ?: if (ua.status == circleGreen.getValue(colorblind)) val status = newStatus ?: if (isAirplaneModeOn)
ua.status
else if (isAirplaneModeOn)
R.drawable.circle_white R.drawable.circle_white
else if (ua.status == R.drawable.circle_white) else if (previousMobileServiceState == ServiceState.STATE_IN_SERVICE)
// Show "Red" (not yet in service) circleGreen.getValue(colorblind)
circleRed.getValue(colorblind)
else else
ua.status circleRed.getValue(colorblind)
if (ua.status != status) { if (ua.status != status) {
Log.d(TAG, "Updating Mobile status to $status") Log.d(TAG, "Updating Mobile status to $status")
ua.updateStatus(status) ua.updateStatus(status)
@ -3222,6 +3228,8 @@ class BaresipService: Service() {
var addressFamily = "" var addressFamily = ""
var dnsServers = listOf<InetAddress>() var dnsServers = listOf<InetAddress>()
val messagesLock = Any()
// <aor, password> of those accounts that have auth username without auth password // <aor, password> of those accounts that have auth username without auth password
val aorPasswords = mutableMapOf<String, String>() val aorPasswords = mutableMapOf<String, String>()
var aecAvailable = false var aecAvailable = false

View File

@ -26,6 +26,7 @@ class BlockRule(val aor: String = "", val pattern: String) {
} }
fun save() { fun save() {
if (!BaresipService.isNativeReady) return
Log.d(TAG, "Saving ${BaresipService.blockRules.size} block rules") Log.d(TAG, "Saving ${BaresipService.blockRules.size} block rules")
val file = File(BaresipService.filesPath + "/blocking") val file = File(BaresipService.filesPath + "/blocking")
try { try {
@ -46,7 +47,11 @@ class BlockRule(val aor: String = "", val pattern: String) {
if (file.exists()) if (file.exists())
try { try {
val jsonString = file.readText() val jsonString = file.readText()
BaresipService.blockRules = Json.decodeFromString<MutableList<BlockRule>>(jsonString) val blockRules = Json.decodeFromString<List<BlockRule>>(jsonString)
synchronized(BaresipService.blockRules) {
BaresipService.blockRules.clear()
BaresipService.blockRules.addAll(blockRules)
}
Log.d(TAG, "Restored ${BaresipService.blockRules.size} block rules") Log.d(TAG, "Restored ${BaresipService.blockRules.size} block rules")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Deserialization exception: $e") Log.e(TAG, "Deserialization exception: $e")

View File

@ -33,6 +33,7 @@ class Blocked (
} }
fun save() { fun save() {
if (!BaresipService.isNativeReady) return
Log.d(TAG, "Saving ${BaresipService.blocked.size} blocked calls and messages") Log.d(TAG, "Saving ${BaresipService.blocked.size} blocked calls and messages")
val file = File(BaresipService.filesPath + "/blocked") val file = File(BaresipService.filesPath + "/blocked")
try { try {
@ -54,7 +55,10 @@ class Blocked (
try { try {
val jsonString = file.readText() val jsonString = file.readText()
val blockedList = Json.decodeFromString<List<Blocked>>(jsonString) val blockedList = Json.decodeFromString<List<Blocked>>(jsonString)
BaresipService.blocked = ArrayList(blockedList) synchronized(BaresipService.blocked) {
BaresipService.blocked.clear()
BaresipService.blocked.addAll(blockedList)
}
Log.d(TAG, "Restored ${BaresipService.blocked.size} blocked calls and messages") Log.d(TAG, "Restored ${BaresipService.blocked.size} blocked calls and messages")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Deserialization exception: $e") Log.e(TAG, "Deserialization exception: $e")

View File

@ -76,6 +76,7 @@ class CallHistoryNew(val aor: String, val peerUri: String, val direction: String
} }
fun save() { fun save() {
if (!BaresipService.isNativeReady) return
val historyCopy = synchronized(BaresipService.callHistory) { val historyCopy = synchronized(BaresipService.callHistory) {
ArrayList(BaresipService.callHistory) ArrayList(BaresipService.callHistory)
} }
@ -105,7 +106,10 @@ class CallHistoryNew(val aor: String, val peerUri: String, val direction: String
if (content.startsWith("[")) { if (content.startsWith("[")) {
try { try {
val restoredHistory = Json.decodeFromString<List<CallHistoryNew>>(content) val restoredHistory = Json.decodeFromString<List<CallHistoryNew>>(content)
BaresipService.callHistory = ArrayList(restoredHistory) synchronized(BaresipService.callHistory) {
BaresipService.callHistory.clear()
BaresipService.callHistory.addAll(restoredHistory)
}
Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls from JSON") Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls from JSON")
return return
} catch (e: Exception) { } catch (e: Exception) {
@ -118,7 +122,10 @@ class CallHistoryNew(val aor: String, val peerUri: String, val direction: String
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val restoredHistory = ois.readObject() as? List<CallHistoryNew> val restoredHistory = ois.readObject() as? List<CallHistoryNew>
if (restoredHistory != null) { if (restoredHistory != null) {
BaresipService.callHistory = ArrayList(restoredHistory) synchronized(BaresipService.callHistory) {
BaresipService.callHistory.clear()
BaresipService.callHistory.addAll(restoredHistory)
}
Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls from Java") Log.d(TAG, "Restored history of ${BaresipService.callHistory.size} calls from Java")
save() save()
} }

View File

@ -341,7 +341,7 @@ private fun Calls(
secondAction.value = { secondAction.value = {
if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) { if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) {
alertTitle.value = ctx.getString(R.string.notice) alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = ctx.getString(R.string.airplane_mode) alertMessage.value = Utils.mobileStatusMessage(ctx, ua.status)
showAlert.value = true showAlert.value = true
} }
else { else {
@ -357,8 +357,7 @@ private fun Calls(
if (ua.account.isMobile) { if (ua.account.isMobile) {
if (ua.status != circleGreen.getValue(colorblind)) { if (ua.status != circleGreen.getValue(colorblind)) {
alertTitle.value = ctx.getString(R.string.notice) alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = alertMessage.value = Utils.mobileStatusMessage(ctx, ua.status)
ctx.getString(R.string.airplane_mode)
showAlert.value = true showAlert.value = true
} }
else if (!Utils.isDefaultSmsApp(ctx)) { else if (!Utils.isDefaultSmsApp(ctx)) {

View File

@ -488,7 +488,6 @@ private fun NewMessage(
val showDialog = remember { mutableStateOf(false) } val showDialog = remember { mutableStateOf(false) }
val dialogMessage = remember { mutableStateOf("") } val dialogMessage = remember { mutableStateOf("") }
val airplaneMode = stringResource(R.string.airplane_mode)
val messageFailed = stringResource(R.string.message_failed) val messageFailed = stringResource(R.string.message_failed)
val noTelephonyProvider = stringResource(R.string.no_telephony_provider) val noTelephonyProvider = stringResource(R.string.no_telephony_provider)
@ -570,7 +569,7 @@ private fun NewMessage(
addMessage(msg) addMessage(msg)
if (ua.account.isMobile) { if (ua.account.isMobile) {
if (ua.status != circleGreen.getValue(colorblind)) { if (ua.status != circleGreen.getValue(colorblind)) {
dialogMessage.value = airplaneMode dialogMessage.value = Utils.mobileStatusMessage(ctx, ua.status)
showDialog.value = true showDialog.value = true
} }
else { else {

View File

@ -732,7 +732,7 @@ private fun UrisSection(
onClick = { onClick = {
if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) { if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) {
alertTitle.value = ctx.getString(R.string.notice) alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = ctx.getString(R.string.airplane_mode) alertMessage.value = Utils.mobileStatusMessage(ctx, ua.status)
showAlert.value = true showAlert.value = true
} }
else if (ua.account.isMobile && !Utils.isDefaultSmsApp(ctx)) { else if (ua.account.isMobile && !Utils.isDefaultSmsApp(ctx)) {
@ -770,7 +770,7 @@ private fun UrisSection(
onClick = { onClick = {
if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) { if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) {
alertTitle.value = ctx.getString(R.string.notice) alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = ctx.getString(R.string.airplane_mode) alertMessage.value = Utils.mobileStatusMessage(ctx, ua.status)
showAlert.value = true showAlert.value = true
} }
else { else {

View File

@ -2119,7 +2119,7 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
} }
else if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) { else if (ua.account.isMobile && ua.status != circleGreen.getValue(colorblind)) {
alertTitle.value = ctx.getString(R.string.notice) alertTitle.value = ctx.getString(R.string.notice)
alertMessage.value = ctx.getString(R.string.airplane_mode) alertMessage.value = Utils.mobileStatusMessage(ctx, ua.status)
showAlert.value = true showAlert.value = true
return return
} }

View File

@ -10,7 +10,7 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
var new: Boolean): java.io.Serializable { var new: Boolean): java.io.Serializable {
fun add() { fun add() {
val updatedMessages = synchronized(BaresipService.messages) { val updatedMessages = synchronized(BaresipService.messagesLock) {
BaresipService.messages.toMutableList() BaresipService.messages.toMutableList()
} }
updatedMessages.add(this) updatedMessages.add(this)
@ -26,18 +26,18 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
} }
if (remove != null) if (remove != null)
updatedMessages.remove(remove) updatedMessages.remove(remove)
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
BaresipService.messages = updatedMessages.toList() BaresipService.messages = updatedMessages.toList()
} }
save() save()
} }
fun delete() { fun delete() {
val updatedMessages = synchronized(BaresipService.messages) { val updatedMessages = synchronized(BaresipService.messagesLock) {
BaresipService.messages.toMutableList() BaresipService.messages.toMutableList()
} }
updatedMessages.remove(this) updatedMessages.remove(this)
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
BaresipService.messages = updatedMessages.toList() BaresipService.messages = updatedMessages.toList()
} }
save() save()
@ -55,25 +55,25 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
} }
fun clearMessagesOfAor(aor: String) { fun clearMessagesOfAor(aor: String) {
val updatedMessages = synchronized(BaresipService.messages) { val updatedMessages = synchronized(BaresipService.messagesLock) {
BaresipService.messages.toMutableList() BaresipService.messages.toMutableList()
} }
val it = updatedMessages.iterator() val it = updatedMessages.iterator()
while (it.hasNext()) if (it.next().aor == aor) it.remove() while (it.hasNext()) if (it.next().aor == aor) it.remove()
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
BaresipService.messages = updatedMessages.toList() BaresipService.messages = updatedMessages.toList()
} }
save() save()
} }
fun deleteAorMessage(aor: String, time: Long) { fun deleteAorMessage(aor: String, time: Long) {
val updatedMessages = synchronized(BaresipService.messages) { val updatedMessages = synchronized(BaresipService.messagesLock) {
BaresipService.messages.toMutableList() BaresipService.messages.toMutableList()
} }
for (message in updatedMessages.reversed()) for (message in updatedMessages.reversed())
if (message.aor == aor && message.timeStamp == time) { if (message.aor == aor && message.timeStamp == time) {
updatedMessages.remove(message) updatedMessages.remove(message)
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
BaresipService.messages = updatedMessages.toList() BaresipService.messages = updatedMessages.toList()
} }
save() save()
@ -82,13 +82,13 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
} }
fun updateAorMessage(aor: String, time: Long) { fun updateAorMessage(aor: String, time: Long) {
val updatedMessages = synchronized(BaresipService.messages) { val updatedMessages = synchronized(BaresipService.messagesLock) {
BaresipService.messages.toMutableList() BaresipService.messages.toMutableList()
} }
for (message in updatedMessages.reversed()) for (message in updatedMessages.reversed())
if (message.aor == aor && message.timeStamp == time) { if (message.aor == aor && message.timeStamp == time) {
message.new = false message.new = false
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
BaresipService.messages = updatedMessages.toList() BaresipService.messages = updatedMessages.toList()
} }
save() save()
@ -97,7 +97,7 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
} }
fun unreadMessages(aor: String): Boolean { fun unreadMessages(aor: String): Boolean {
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
for (message in BaresipService.messages.reversed()) for (message in BaresipService.messages.reversed())
if (message.aor == aor && message.new) if (message.aor == aor && message.new)
return true return true
@ -106,7 +106,7 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
} }
fun unreadMessagesFromPeer(aor: String, peerUri: String): Boolean { fun unreadMessagesFromPeer(aor: String, peerUri: String): Boolean {
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
for (message in BaresipService.messages.reversed()) for (message in BaresipService.messages.reversed())
if (message.aor == aor && message.peerUri == peerUri && message.new) if (message.aor == aor && message.peerUri == peerUri && message.new)
return true return true
@ -115,7 +115,7 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
} }
fun updateMessagesFromPearRead(aor: String, peerUri: String): Boolean { fun updateMessagesFromPearRead(aor: String, peerUri: String): Boolean {
val updatedMessages = synchronized(BaresipService.messages) { val updatedMessages = synchronized(BaresipService.messagesLock) {
BaresipService.messages.toMutableList() BaresipService.messages.toMutableList()
} }
var updated = false var updated = false
@ -125,7 +125,7 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
updated = true updated = true
} }
if (updated) { if (updated) {
synchronized(BaresipService.messages) { synchronized(BaresipService.messagesLock) {
BaresipService.messages = updatedMessages.toList() BaresipService.messages = updatedMessages.toList()
} }
save() save()
@ -134,7 +134,8 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
} }
fun save() { fun save() {
val messagesCopy = synchronized(BaresipService.messages) { if (!BaresipService.isNativeReady) return
val messagesCopy = synchronized(BaresipService.messagesLock) {
BaresipService.messages.toList() BaresipService.messages.toList()
} }
val file = File(BaresipService.filesPath, "messages") val file = File(BaresipService.filesPath, "messages")
@ -143,7 +144,7 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
file.writeText(jsonString) file.writeText(jsonString)
Log.d(TAG, "Saved ${messagesCopy.size} messages in JSON") Log.d(TAG, "Saved ${messagesCopy.size} messages in JSON")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Serialization exception", e) Log.e(TAG, "Serialization exception: $e")
try { try {
val fos = FileOutputStream(file) val fos = FileOutputStream(file)
val oos = ObjectOutputStream(fos) val oos = ObjectOutputStream(fos)
@ -164,7 +165,9 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
if (content.startsWith("[")) { if (content.startsWith("[")) {
try { try {
val restoredMessages = Json.decodeFromString<List<Message>>(content) val restoredMessages = Json.decodeFromString<List<Message>>(content)
BaresipService.messages = restoredMessages synchronized(BaresipService.messagesLock) {
BaresipService.messages = restoredMessages
}
Log.d(TAG, "Restored ${BaresipService.messages.size} messages from JSON") Log.d(TAG, "Restored ${BaresipService.messages.size} messages from JSON")
return return
} catch (e: Exception) { } catch (e: Exception) {
@ -177,7 +180,9 @@ class Message(val aor: String, val peerUri: String, val message: String, val tim
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val restoredMessages = ois.readObject() as? List<Message> val restoredMessages = ois.readObject() as? List<Message>
if (restoredMessages != null) { if (restoredMessages != null) {
BaresipService.messages = restoredMessages synchronized(BaresipService.messagesLock) {
BaresipService.messages = restoredMessages
}
Log.d(TAG, "Restored ${BaresipService.messages.size} messages from Java") Log.d(TAG, "Restored ${BaresipService.messages.size} messages from Java")
save() save()
} }

View File

@ -903,6 +903,13 @@ object Utils {
) != 0 ) != 0
} }
fun mobileStatusMessage(ctx: Context, status: Int): String {
return if (status == R.drawable.circle_white)
ctx.getString(R.string.airplane_mode)
else
ctx.getString(R.string.mobile_service_not_available)
}
@Suppress("unused") @Suppress("unused")
fun isPSTNCallActive(ctx: Context): Boolean { fun isPSTNCallActive(ctx: Context): Boolean {
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as? TelecomManager ?: return false val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as? TelecomManager ?: return false

View File

@ -642,6 +642,7 @@
<string name="no_notifications">Et voi käyttää tätä sovellusta ilman Ilmoitukset-lupaa.</string> <string name="no_notifications">Et voi käyttää tätä sovellusta ilman Ilmoitukset-lupaa.</string>
<string name="airplane_mode">Laite on lentotilassa. Poista lentotila, jos haluat käyttää <string name="airplane_mode">Laite on lentotilassa. Poista lentotila, jos haluat käyttää
mobiilitiliä.</string> mobiilitiliä.</string>
<string name="mobile_service_not_available">Mobiilipalvelu ei ole käytettävissä.</string>
<string name="no_calls">Et voi soittaa puheluita tai vastata niihin ilman Mikrofoni-lupaa.</string> <string name="no_calls">Et voi soittaa puheluita tai vastata niihin ilman Mikrofoni-lupaa.</string>
<string name="no_video_calls">Salli Kamera-lupa jotta voit soittaa videopuheluita ja vastata niihin.</string> <string name="no_video_calls">Salli Kamera-lupa jotta voit soittaa videopuheluita ja vastata niihin.</string>
<string name="no_backup">Et voi tallentaa sovelluksen tietoja ilman Tallennustila-lupaa.</string> <string name="no_backup">Et voi tallentaa sovelluksen tietoja ilman Tallennustila-lupaa.</string>

View File

@ -627,6 +627,7 @@
<string name="no_notifications">You are not able to use this application without \"Notifications\" <string name="no_notifications">You are not able to use this application without \"Notifications\"
permission.</string> permission.</string>
<string name="airplane_mode">Airplane mode is ON. Turn it OFF to use mobile account.</string> <string name="airplane_mode">Airplane mode is ON. Turn it OFF to use mobile account.</string>
<string name="mobile_service_not_available">Mobile service is not available.</string>
<string name="no_calls">baresip needs \"Microphone\" permission for voice calls.</string> <string name="no_calls">baresip needs \"Microphone\" permission for voice calls.</string>
<string name="no_video_calls">Grant \"Camera\" permission to make or answer video calls.</string> <string name="no_video_calls">Grant \"Camera\" permission to make or answer video calls.</string>
<string name="no_backup">You are not able create backup without \"Storage\" permission.</string> <string name="no_backup">You are not able create backup without \"Storage\" permission.</string>