Don't show call timer notification on locked screen after call is closed

This commit is contained in:
Juha Heinanen
2026-05-13 10:33:58 +03:00
parent b7bd1181e0
commit 70a4efd5ba
3 changed files with 131 additions and 99 deletions

View File

@ -1089,6 +1089,7 @@ class BaresipService: Service() {
Handler(Looper.getMainLooper()).postDelayed({
connection.destroy()
ConnectionService.connections.remove(callp)
updateStatusNotification()
}, 500)
}
nm.cancel(CALL_NOTIFICATION_ID)
@ -1565,117 +1566,142 @@ class BaresipService: Service() {
fun updateStatusNotification() {
val activeCall = Call.calls().find {
it.status.value == "connected" || it.status.value == "outgoing" || it.status.value == "answered"
}
val builder = NotificationCompat.Builder(this, LOW_CHANNEL_ID)
val intent = Intent(applicationContext, MainActivity::class.java)
.setAction(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
val pi = PendingIntent.getActivity(applicationContext, STATUS_REQ_CODE, intent, PendingIntent.FLAG_IMMUTABLE)
val deleteIntent = Intent(this, BaresipService::class.java).setAction("Notification Dismissed")
val dpi = PendingIntent.getService(this, STATUS_REQ_CODE, deleteIntent, PendingIntent.FLAG_IMMUTABLE)
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_notification_b)
.setContentIntent(pi)
.setDeleteIntent(dpi)
.setOngoing(true)
if (activeCall != null) {
val peerUri = activeCall.peerUri
val caller = Utils.friendlyUri(this, peerUri, activeCall.ua.account)
val person = Person.Builder().setName(caller).build()
val hangupIntent = Intent(this, BaresipService::class.java)
hangupIntent.action = "Call Hangup"
hangupIntent.putExtra("callp", activeCall.callp)
val hpi = PendingIntent.getService(this, REJECT_REQ_CODE, hangupIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
builder.setStyle(NotificationCompat.CallStyle.forOngoingCall(person, hpi))
.setCategory(Notification.CATEGORY_CALL)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setWhen(activeCall.startTime?.timeInMillis ?: System.currentTimeMillis())
.setUsesChronometer(true)
.setContentText(
if (activeCall.onhold)
getString(R.string.call_is_on_hold)
else
when (activeCall.status.value) {
"outgoing", "incoming" -> getString(R.string.call_is_ringing)
"connected", "answered" -> getString(R.string.call_is_connected)
else -> getString(R.string.call)
}
)
} else {
builder.setStyle(null)
builder.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCategory(Notification.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setWhen(0)
.setShowWhen(false)
.setUsesChronometer(false)
.setContentTitle("")
.setContentText("")
val notificationLayout = RemoteViews(packageName, R.layout.status_notification)
for (i in 0..3) {
val resId = when (i) {
0 -> R.id.status0
1 -> R.id.status1
2 -> R.id.status2
else -> R.id.status3
}
if (i < uas.value.size) {
notificationLayout.setImageViewResource(resId, uas.value[i].status)
notificationLayout.setViewVisibility(resId, View.VISIBLE)
} else {
notificationLayout.setViewVisibility(resId, View.INVISIBLE)
Handler(Looper.getMainLooper()).post {
val activeCall = synchronized(calls) {
calls.find {
it.status.value == "connected" || it.status.value == "outgoing" || it.status.value == "answered"
}
}
if (uas.value.size > 4)
notificationLayout.setViewVisibility(R.id.etc, View.VISIBLE)
else
notificationLayout.setViewVisibility(R.id.etc, View.INVISIBLE)
builder.setCustomContentView(notificationLayout)
}
val builder = NotificationCompat.Builder(this, LOW_CHANNEL_ID)
val intent = Intent(applicationContext, MainActivity::class.java)
.setAction(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER)
if (VERSION.SDK_INT >= 31)
builder.foregroundServiceBehavior = Notification.FOREGROUND_SERVICE_IMMEDIATE
val pi = PendingIntent.getActivity(
applicationContext,
STATUS_REQ_CODE,
intent,
PendingIntent.FLAG_IMMUTABLE
)
val deleteIntent =
Intent(this, BaresipService::class.java).setAction("Notification Dismissed")
val dpi = PendingIntent.getService(
this,
STATUS_REQ_CODE,
deleteIntent,
PendingIntent.FLAG_IMMUTABLE
)
builder.setOngoing(true)
val notification = builder.build()
notification.flags = notification.flags or Notification.FLAG_NO_CLEAR or Notification.FLAG_ONGOING_EVENT
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.ic_notification_b)
.setContentIntent(pi)
.setDeleteIntent(dpi)
.setOngoing(true)
try {
if (activeCall != null) {
if (VERSION.SDK_INT >= 29)
startForeground(STATUS_NOTIFICATION_ID, notification, foregroundServiceType(activeCall))
else
startForeground(STATUS_NOTIFICATION_ID, notification)
isNotificationInCall = true
val peerUri = activeCall.peerUri
val caller = Utils.friendlyUri(this, peerUri, activeCall.ua.account)
val person = Person.Builder().setName(caller).build()
val hangupIntent = Intent(this, BaresipService::class.java)
hangupIntent.action = "Call Hangup"
hangupIntent.putExtra("callp", activeCall.callp)
val hpi = PendingIntent.getService(
this, REJECT_REQ_CODE, hangupIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
builder.setStyle(NotificationCompat.CallStyle.forOngoingCall(person, hpi))
.setCategory(Notification.CATEGORY_CALL)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setWhen(activeCall.startTime?.timeInMillis ?: System.currentTimeMillis())
.setUsesChronometer(true)
.setContentText(
if (activeCall.onhold)
getString(R.string.call_is_on_hold)
else
when (activeCall.status.value) {
"outgoing", "incoming" -> getString(R.string.call_is_ringing)
"connected", "answered" -> getString(R.string.call_is_connected)
else -> getString(R.string.call)
}
)
} else {
// If we are currently in a call notification mode, downgrade to standby FGS
// but DO NOT call stopForeground as that would drop FGS status.
if (isNotificationInCall) {
builder.setStyle(null)
builder.setStyle(NotificationCompat.DecoratedCustomViewStyle())
.setCategory(Notification.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setWhen(0)
.setShowWhen(false)
.setUsesChronometer(false)
.setContentTitle("")
.setContentText("")
val notificationLayout = RemoteViews(packageName, R.layout.status_notification)
for (i in 0..3) {
val resId = when (i) {
0 -> R.id.status0
1 -> R.id.status1
2 -> R.id.status2
else -> R.id.status3
}
if (i < uas.value.size) {
notificationLayout.setImageViewResource(resId, uas.value[i].status)
notificationLayout.setViewVisibility(resId, View.VISIBLE)
} else {
notificationLayout.setViewVisibility(resId, View.INVISIBLE)
}
}
if (uas.value.size > 4)
notificationLayout.setViewVisibility(R.id.etc, View.VISIBLE)
else
notificationLayout.setViewVisibility(R.id.etc, View.INVISIBLE)
builder.setCustomContentView(notificationLayout)
}
if (VERSION.SDK_INT >= 31)
builder.foregroundServiceBehavior = Notification.FOREGROUND_SERVICE_IMMEDIATE
builder.setOngoing(true)
val notification = builder.build()
notification.flags =
notification.flags or Notification.FLAG_NO_CLEAR or Notification.FLAG_ONGOING_EVENT
try {
if (activeCall != null) {
if (VERSION.SDK_INT >= 29)
startForeground(STATUS_NOTIFICATION_ID, notification, foregroundServiceType())
startForeground(
STATUS_NOTIFICATION_ID,
notification,
foregroundServiceType(activeCall)
)
else
startForeground(STATUS_NOTIFICATION_ID, notification)
isNotificationInCall = false
isNotificationInCall = true
} else {
// Already in standby, just update the notification content
nm.notify(STATUS_NOTIFICATION_ID, notification)
// If we are currently in a call notification mode, downgrade to standby FGS.
// We call stopForeground(REMOVE) to explicitly clear the system's telephony UI context
// that might be persisting the timer/peer info on the lock screen.
if (isNotificationInCall) {
stopForeground(STOP_FOREGROUND_REMOVE)
isNotificationInCall = false
}
if (VERSION.SDK_INT >= 29)
startForeground(
STATUS_NOTIFICATION_ID,
notification,
foregroundServiceType()
)
else
startForeground(STATUS_NOTIFICATION_ID, notification)
}
} catch (e: Exception) {
Log.e(TAG, "Failed to update foreground notification: ${e.message}")
// Fallback to standard notification if promotion/update fails
nm.notify(STATUS_NOTIFICATION_ID, notification)
}
} catch (e: Exception) {
Log.e(TAG, "Failed to update foreground notification: ${e.message}")
// Fallback to standard notification if promotion/update fails
nm.notify(STATUS_NOTIFICATION_ID, notification)
}
}
@ -1841,6 +1867,7 @@ class BaresipService: Service() {
proximitySensing(false)
stopMediaPlayer()
}
updateStatusNotification()
messageUpdate.postValue(System.currentTimeMillis())
}

View File

@ -48,11 +48,15 @@ open class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir
val focusDtmf: MutableState<Boolean> = mutableStateOf(false)
fun add() {
BaresipService.calls.add(this)
synchronized(BaresipService.calls) {
BaresipService.calls.add(this)
}
}
fun remove() {
BaresipService.calls.remove(this)
synchronized(BaresipService.calls) {
BaresipService.calls.remove(this)
}
}
open fun connect(uri: String): Boolean {

View File

@ -195,6 +195,7 @@ class ConnectionService : ConnectionService() {
if (call != null)
Api.ua_hangup(uap, callp, 0, "")
setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
connections.remove(callp)
destroy()
// Allow other disconnects after a short period to prevent the "Telecom Cascade" effect
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({