More work on missed mobile calls

This commit is contained in:
Juha Heinanen
2026-07-15 15:28:34 +03:00
6 changed files with 149 additions and 54 deletions

View File

@ -94,6 +94,11 @@
<data android:scheme="tel" /> <data android:scheme="tel" />
<data android:scheme="sip" /> <data android:scheme="sip" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/calls" />
</intent-filter>
</activity> </activity>
<activity <activity

View File

@ -24,8 +24,8 @@ import android.media.AudioAttributes
import android.media.AudioDeviceInfo import android.media.AudioDeviceInfo
import android.media.AudioFocusRequest import android.media.AudioFocusRequest
import android.media.AudioManager import android.media.AudioManager
import android.media.AudioManager.MODE_IN_COMMUNICATION
import android.media.AudioManager.MODE_IN_CALL import android.media.AudioManager.MODE_IN_CALL
import android.media.AudioManager.MODE_IN_COMMUNICATION
import android.media.AudioManager.MODE_NORMAL import android.media.AudioManager.MODE_NORMAL
import android.media.MediaPlayer import android.media.MediaPlayer
import android.media.Ringtone import android.media.Ringtone
@ -80,6 +80,10 @@ import androidx.core.net.toUri
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.tutpro.baresip.Utils.e164Uri import com.tutpro.baresip.Utils.e164Uri
import com.tutpro.baresip.Utils.toCircle import com.tutpro.baresip.Utils.toCircle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.net.InetAddress import java.net.InetAddress
import java.nio.charset.Charset import java.nio.charset.Charset
@ -90,10 +94,6 @@ import java.util.TimerTask
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class BaresipService: Service() { class BaresipService: Service() {
@ -723,6 +723,24 @@ class BaresipService: Service() {
nm.cancel(MESSAGE_NOTIFICATION_ID) nm.cancel(MESSAGE_NOTIFICATION_ID)
} }
"Clear Unread" -> {
val uap = intent!!.getLongExtra("uap", 0L)
val ua = UserAgent.ofUap(uap)
if (ua != null) {
val peerUri = intent.getStringExtra("peer")
if (peerUri != null)
Message.updateMessagesFromPearRead(ua.account.aor, peerUri)
else {
for (m in messages)
if (m.aor == ua.account.aor)
m.new = false
Message.save()
}
ua.account.unreadMessages = Message.unreadMessages(ua.account.aor)
}
nm.cancel(MESSAGE_NOTIFICATION_ID)
}
"Message Inline Reply" -> { "Message Inline Reply" -> {
val remoteInputResults = RemoteInput.getResultsFromIntent(intent!!) val remoteInputResults = RemoteInput.getResultsFromIntent(intent!!)
if (remoteInputResults != null) { if (remoteInputResults != null) {
@ -777,6 +795,14 @@ class BaresipService: Service() {
addMobileUserAgent() addMobileUserAgent()
} }
"Clear Missed" -> {
val uap = intent!!.getLongExtra("uap", 0L)
val ua = UserAgent.ofUap(uap)
if (ua != null)
ua.account.missedCalls = false
nm.cancel(CALL_MISSED_NOTIFICATION_ID)
}
"Start Call" -> { "Start Call" -> {
val uap = intent!!.getLongExtra("uap", 0L) val uap = intent!!.getLongExtra("uap", 0L)
val uri = intent.getStringExtra("uri")!! val uri = intent.getStringExtra("uri")!!
@ -1560,10 +1586,16 @@ class BaresipService: Service() {
.setGroupConversation(false) .setGroupConversation(false)
.addMessage(text, timeStamp, sender) .addMessage(text, timeStamp, sender)
val clearIntent = Intent(this, BaresipService::class.java)
clearIntent.action = "Clear Unread"
clearIntent.putExtra("uap", uap).putExtra("peer", peerUri)
val dpi = PendingIntent.getService(this, MESSAGE_NOTIFICATION_ID, clearIntent, piFlags)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_message) .setSmallIcon(R.drawable.ic_notification_message)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary)) .setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setContentIntent(pi) .setContentIntent(pi)
.setDeleteIntent(dpi)
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI) .setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
.setAutoCancel(true) .setAutoCancel(true)
.setStyle(messagingStyle) .setStyle(messagingStyle)
@ -1579,9 +1611,7 @@ class BaresipService: Service() {
} }
nb.setNumber(unreadMessages + 1) nb.setNumber(unreadMessages + 1)
val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY) val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(getString(R.string.reply)).build()
.setLabel(getString(R.string.reply))
.build()
val directReplyIntent = Intent(this, BaresipService::class.java) val directReplyIntent = Intent(this, BaresipService::class.java)
directReplyIntent.action = "Message Inline Reply" directReplyIntent.action = "Message Inline Reply"
directReplyIntent.putExtra("uap", uap).putExtra("peer", peerUri).putExtra("time", timeStamp) directReplyIntent.putExtra("uap", uap).putExtra("peer", peerUri).putExtra("time", timeStamp)
@ -1602,10 +1632,8 @@ class BaresipService: Service() {
val saveIntent = Intent(this, BaresipService::class.java) val saveIntent = Intent(this, BaresipService::class.java)
saveIntent.action = "Message Save" saveIntent.action = "Message Save"
saveIntent.putExtra("uap", uap) saveIntent.putExtra("uap", uap).putExtra("time", timeStamp.toString())
.putExtra("time", timeStamp.toString()) val savePendingIntent = PendingIntent.getService(this, SAVE_REQ_CODE, saveIntent, piFlags)
val savePendingIntent = PendingIntent
.getService(this, SAVE_REQ_CODE, saveIntent, piFlags)
val saveAction = NotificationCompat.Action.Builder( val saveAction = NotificationCompat.Action.Builder(
R.drawable.ic_notification_save, R.drawable.ic_notification_save,
getString(R.string.save), getString(R.string.save),
@ -1614,10 +1642,8 @@ class BaresipService: Service() {
val deleteIntent = Intent(this, BaresipService::class.java) val deleteIntent = Intent(this, BaresipService::class.java)
deleteIntent.action = "Message Delete" deleteIntent.action = "Message Delete"
deleteIntent.putExtra("uap", uap) deleteIntent.putExtra("uap", uap).putExtra("time", timeStamp.toString())
.putExtra("time", timeStamp.toString()) val deletePendingIntent = PendingIntent.getService(this, DELETE_REQ_CODE, deleteIntent, piFlags)
val deletePendingIntent = PendingIntent
.getService(this, DELETE_REQ_CODE, deleteIntent, piFlags)
val deleteAction = NotificationCompat.Action.Builder( val deleteAction = NotificationCompat.Action.Builder(
R.drawable.ic_notification_delete, R.drawable.ic_notification_delete,
getString(R.string.delete), getString(R.string.delete),
@ -2250,6 +2276,7 @@ class BaresipService: Service() {
history.add() history.add()
if (call.dir == "in" && call.startTime == null && !call.rejected) { if (call.dir == "in" && call.startTime == null && !call.rejected) {
call.ua.account.missedCalls = true call.ua.account.missedCalls = true
if (!call.ua.account.isMobile)
showMissedCallNotification(uap, call.peerUri, call.ua.account.aor) showMissedCallNotification(uap, call.peerUri, call.ua.account.aor)
} }
} }
@ -3080,10 +3107,17 @@ class BaresipService: Service() {
intent, intent,
piFlags piFlags
) )
val deleteIntent = Intent(this, BaresipService::class.java)
deleteIntent.action = "Clear Missed"
deleteIntent.putExtra("uap", uap)
val dpi = PendingIntent.getService(this, CALL_MISSED_NOTIFICATION_ID, deleteIntent, piFlags)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
nb.setSmallIcon(R.drawable.ic_notification_call_missed) nb.setSmallIcon(R.drawable.ic_notification_call_missed)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary)) .setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setContentIntent(pi) .setContentIntent(pi)
.setDeleteIntent(dpi)
.setCategory(NotificationCompat.CATEGORY_MISSED_CALL) .setCategory(NotificationCompat.CATEGORY_MISSED_CALL)
.setAutoCancel(true) .setAutoCancel(true)

View File

@ -114,8 +114,13 @@ private fun CallsScreen(navController: NavController, viewModel: ViewModel, aor:
} }
} }
val ctx = LocalContext.current
BackHandler(enabled = true) { BackHandler(enabled = true) {
ua.account.missedCalls = false val serviceIntent = Intent(ctx, BaresipService::class.java)
serviceIntent.action = "Clear Missed"
serviceIntent.putExtra("uap", ua.uap)
ctx.startService(serviceIntent)
navController.navigateUp() navController.navigateUp()
} }
@ -182,9 +187,13 @@ private fun TopAppBar(navController: NavController, ua: UserAgent, callHistory:
), ),
windowInsets = WindowInsets(0, 0, 0, 0), windowInsets = WindowInsets(0, 0, 0, 0),
navigationIcon = { navigationIcon = {
val ctx = LocalContext.current
IconButton( IconButton(
onClick = { onClick = {
account.missedCalls = false val serviceIntent = Intent(ctx, BaresipService::class.java)
serviceIntent.action = "Clear Missed"
serviceIntent.putExtra("uap", ua.uap)
ctx.startService(serviceIntent)
navController.navigateUp() navController.navigateUp()
} }
) { ) {
@ -495,7 +504,7 @@ private fun Calls(
count++ count++
} }
if (count > 3) if (count > 3)
Text("...", color = MaterialTheme.colorScheme.onBackground) Text(ctx.getString(R.string.dots), color = MaterialTheme.colorScheme.onBackground)
Text( Text(
text = Utils.friendlyUri(ctx, peerUri, ua.account), text = Utils.friendlyUri(ctx, peerUri, ua.account),
modifier = Modifier.padding(start = 8.dp), modifier = Modifier.padding(start = 8.dp),

View File

@ -1,8 +1,8 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.content.Context
import android.content.Intent import android.content.Intent
import android.text.format.DateUtils.isToday import android.text.format.DateUtils.isToday
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
@ -102,7 +102,6 @@ fun NavGraphBuilder.chatScreenRoute(navController: NavController, viewModel: Vie
val aor = backStackEntry.arguments?.getString("aor")!! val aor = backStackEntry.arguments?.getString("aor")!!
val peerUri = backStackEntry.arguments?.getString("peer")!! val peerUri = backStackEntry.arguments?.getString("peer")!!
ChatScreen( ChatScreen(
ctx = LocalContext.current,
navController = navController, navController = navController,
viewModel = viewModel, viewModel = viewModel,
account = Account.ofAor(aor)!!, account = Account.ofAor(aor)!!,
@ -113,13 +112,13 @@ fun NavGraphBuilder.chatScreenRoute(navController: NavController, viewModel: Vie
@Composable @Composable
private fun ChatScreen( private fun ChatScreen(
ctx: Context,
navController: NavController, navController: NavController,
viewModel: ViewModel, viewModel: ViewModel,
account: Account, account: Account,
peerUri: String peerUri: String
) { ) {
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val ctx = LocalContext.current
val aor = account.aor val aor = account.aor
@ -164,6 +163,11 @@ private fun ChatScreen(
} }
BackHandler(enabled = true) { BackHandler(enabled = true) {
val serviceIntent = Intent(ctx, BaresipService::class.java)
serviceIntent.action = "Clear Unread"
serviceIntent.putExtra("uap", account.accp)
serviceIntent.putExtra("peer", peerUri)
ctx.startService(serviceIntent)
backAction(navController, account, peerUri) backAction(navController, account, peerUri)
} }
@ -173,12 +177,11 @@ private fun ChatScreen(
topBar = { topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
Spacer(Modifier.statusBarsPadding()) Spacer(Modifier.statusBarsPadding())
TopAppBar(ctx, navController, viewModel, account, peerUri) TopAppBar(navController, viewModel, account, peerUri)
} }
}, },
bottomBar = { bottomBar = {
NewMessage( NewMessage(
ctx = ctx,
viewModel, viewModel,
account = account, account = account,
peerUri = peerUri, peerUri = peerUri,
@ -188,7 +191,6 @@ private fun ChatScreen(
content = { contentPadding -> content = { contentPadding ->
if (areMessagesLoaded) if (areMessagesLoaded)
ChatContent( ChatContent(
ctx,
contentPadding, contentPadding,
account, peerUri, account, peerUri,
chatMessages, chatMessages,
@ -201,12 +203,12 @@ private fun ChatScreen(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun TopAppBar( private fun TopAppBar(
ctx: Context,
navController: NavController, navController: NavController,
viewModel: ViewModel, viewModel: ViewModel,
account: Account, account: Account,
peerUri: String peerUri: String
) { ) {
val ctx = LocalContext.current
val aor = account.aor val aor = account.aor
TopAppBar( TopAppBar(
title = { title = {
@ -223,7 +225,17 @@ private fun TopAppBar(
actionIconContentColor = MaterialTheme.colorScheme.onPrimary actionIconContentColor = MaterialTheme.colorScheme.onPrimary
), ),
navigationIcon = { navigationIcon = {
IconButton(onClick = { backAction(navController, account, peerUri) }) { val ctx = LocalContext.current
IconButton(
onClick = {
val serviceIntent = Intent(ctx, BaresipService::class.java)
serviceIntent.action = "Clear Unread"
serviceIntent.putExtra("uap", account.accp)
serviceIntent.putExtra("peer", peerUri)
ctx.startService(serviceIntent)
backAction(navController, account, peerUri)
}
) {
Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
} }
}, },
@ -282,7 +294,6 @@ private fun TopAppBar(
@Composable @Composable
private fun ChatContent( private fun ChatContent(
ctx: Context,
contentPadding: PaddingValues, contentPadding: PaddingValues,
account: Account, account: Account,
peerUri: String, peerUri: String,
@ -293,14 +304,15 @@ private fun ChatContent(
modifier = Modifier.fillMaxWidth().padding(contentPadding), modifier = Modifier.fillMaxWidth().padding(contentPadding),
verticalArrangement = Arrangement.Bottom verticalArrangement = Arrangement.Bottom
) { ) {
Account(ctx, account) Account(account)
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Messages(ctx, account, peerUri, messages, onMessageDeleted) Messages(account, peerUri, messages, onMessageDeleted)
} }
} }
@Composable @Composable
private fun Account(ctx: Context, account: Account) { private fun Account(account: Account) {
val ctx = LocalContext.current
Text( Text(
text = ctx.getString(R.string.account) + " " + account.text(), text = ctx.getString(R.string.account) + " " + account.text(),
modifier = Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 8.dp), modifier = Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 8.dp),
@ -312,12 +324,12 @@ private fun Account(ctx: Context, account: Account) {
@Composable @Composable
private fun Messages( private fun Messages(
ctx: Context,
account: Account, account: Account,
peerUri: String, peerUri: String,
messages: List<Message>, messages: List<Message>,
onMessageDeleted: () -> Unit onMessageDeleted: () -> Unit
) { ) {
val ctx = LocalContext.current
val peerName = Utils.friendlyUri(ctx, peerUri, account) val peerName = Utils.friendlyUri(ctx, peerUri, account)
val showDialog = remember { mutableStateOf(false) } val showDialog = remember { mutableStateOf(false) }
@ -444,13 +456,12 @@ private fun Messages(
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun NewMessage( private fun NewMessage(
ctx: Context,
viewModel: ViewModel, viewModel: ViewModel,
account: Account, account: Account,
peerUri: String, peerUri: String,
addMessage: (message: Message) -> Unit addMessage: (message: Message) -> Unit
) { ) {
val ctx = LocalContext.current
val aor = account.aor val aor = account.aor
val ua = UserAgent.ofAor(aor)!! val ua = UserAgent.ofAor(aor)!!

View File

@ -1,6 +1,7 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.content.Context import android.content.Intent
import androidx.activity.compose.BackHandler
import android.text.format.DateUtils.isToday import android.text.format.DateUtils.isToday
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
@ -13,11 +14,9 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -129,8 +128,15 @@ private fun ChatsScreen(navController: NavController, aor: String) {
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
} }
BackHandler(enabled = true) {
val serviceIntent = Intent(ctx, BaresipService::class.java)
serviceIntent.action = "Clear Unread"
serviceIntent.putExtra("uap", account.accp)
ctx.startService(serviceIntent)
navController.navigateUp()
}
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background, containerColor = MaterialTheme.colorScheme.background,
topBar = { topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
@ -138,10 +144,10 @@ private fun ChatsScreen(navController: NavController, aor: String) {
TopAppBar(navController, account, uaMessages) TopAppBar(navController, account, uaMessages)
} }
}, },
bottomBar = { NewChatPeer(ctx, navController, account) }, bottomBar = { NewChatPeer(navController, account) },
content = { contentPadding -> content = { contentPadding ->
if (areMessagesLoaded) if (areMessagesLoaded)
ChatsContent(LocalContext.current, navController, contentPadding, account, uaMessages) ChatsContent(navController, contentPadding, account, uaMessages)
}, },
) )
} }
@ -178,7 +184,16 @@ private fun TopAppBar(
actionIconContentColor = MaterialTheme.colorScheme.onPrimary actionIconContentColor = MaterialTheme.colorScheme.onPrimary
), ),
navigationIcon = { navigationIcon = {
IconButton(onClick = { navController.navigateUp() }) { val ctx = LocalContext.current
IconButton(
onClick = {
val serviceIntent = Intent(ctx, BaresipService::class.java)
serviceIntent.action = "Clear Unread"
serviceIntent.putExtra("uap", account.accp)
ctx.startService(serviceIntent)
navController.navigateUp()
}
) {
Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null)
} }
}, },
@ -214,7 +229,6 @@ private fun TopAppBar(
@Composable @Composable
private fun ChatsContent( private fun ChatsContent(
ctx: Context,
navController: NavController, navController: NavController,
contentPadding: PaddingValues, contentPadding: PaddingValues,
account: Account, account: Account,
@ -225,7 +239,7 @@ private fun ChatsContent(
verticalArrangement = Arrangement.Top verticalArrangement = Arrangement.Top
) { ) {
Account(account) Account(account)
Chats(ctx, navController, account, uaMessages) Chats(navController, account, uaMessages)
} }
} }
@ -242,11 +256,11 @@ private fun Account(account: Account) {
@Composable @Composable
private fun Chats( private fun Chats(
ctx: Context,
navController: NavController, navController: NavController,
account: Account, account: Account,
uaMessages: MutableState<List<Message>> uaMessages: MutableState<List<Message>>
) { ) {
val ctx = LocalContext.current
val aor = account.aor val aor = account.aor
val showDialog = remember { mutableStateOf(false) } val showDialog = remember { mutableStateOf(false) }
@ -409,13 +423,14 @@ private fun Chats(
} }
@Composable @Composable
private fun NewChatPeer(ctx: Context, navController: NavController, account: Account) { private fun NewChatPeer(navController: NavController, account: Account) {
val ctx = LocalContext.current
val alertTitle = remember { mutableStateOf("") } val alertTitle = remember { mutableStateOf("") }
val alertMessage = remember { mutableStateOf("") } val alertMessage = remember { mutableStateOf("") }
val showAlert = remember { mutableStateOf(false) } val showAlert = remember { mutableStateOf(false) }
fun makeChat(ctx: Context, navController: NavController, account: Account, chatPeer: String) { fun makeChat(navController: NavController, account: Account, chatPeer: String) {
val peerUri = if (Utils.isTelNumber(chatPeer)) val peerUri = if (Utils.isTelNumber(chatPeer))
"tel:$chatPeer" "tel:$chatPeer"
else else
@ -587,15 +602,15 @@ private fun NewChatPeer(ctx: Context, navController: NavController, account: Acc
String.format(ctx.getString(R.string.contact_no_sip_or_tel_uri), peerText) String.format(ctx.getString(R.string.contact_no_sip_or_tel_uri), peerText)
showAlert.value = true showAlert.value = true
} else { } else {
makeChat(ctx, navController, account, peerText) makeChat(navController, account, peerText)
} }
} }
else if (uris.size == 1) else if (uris.size == 1)
makeChat(ctx, navController, account, uris[0].uri) makeChat(navController, account, uris[0].uri)
else { else {
items.value = uris.map { it.label.ifEmpty { it.uri.substringAfter(":") } } items.value = uris.map { it.label.ifEmpty { it.uri.substringAfter(":") } }
itemAction.value = { index -> itemAction.value = { index ->
makeChat(ctx, navController, account, uris[index].uri) makeChat(navController, account, uris[index].uri)
} }
showDialog.value = true showDialog.value = true
} }

View File

@ -20,6 +20,7 @@ import android.content.IntentFilter
import android.media.AudioManager import android.media.AudioManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.CallLog
import android.view.KeyEvent import android.view.KeyEvent
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
@ -299,13 +300,16 @@ class MainActivity : ComponentActivity() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
Log.i(TAG, "Main onStart") Log.i(TAG, "Main onStart action/type/data: ${intent.action}/${intent.type}/${intent.data}")
val action = intent.getStringExtra("action") val action = intent.getStringExtra("action")
if (action != null) { if (action != null) {
// MainActivity was not visible when call, message, or transfer request came in // MainActivity was not visible when call, message, or transfer request came in
intent.removeExtra("action") intent.removeExtra("action")
handleIntent(applicationContext, viewModel, intent, action) handleIntent(applicationContext, viewModel, intent, action)
} }
else if (isCallLogIntent(intent)) {
handleCallLogIntent()
}
else if (intent.action == Intent.ACTION_MAIN && !atStartup) { else if (intent.action == Intent.ACTION_MAIN && !atStartup) {
val activeNotifications = nm.activeNotifications val activeNotifications = nm.activeNotifications
if (activeNotifications.any { it.id == CALL_MISSED_NOTIFICATION_ID }) { if (activeNotifications.any { it.id == CALL_MISSED_NOTIFICATION_ID }) {
@ -361,16 +365,20 @@ class MainActivity : ComponentActivity() {
this.setShowWhenLocked(true) this.setShowWhenLocked(true)
this.setTurnScreenOn(true) this.setTurnScreenOn(true)
Log.d(TAG, "onNewIntent with action/data '${intent.action}/${intent.data}'") Log.d(TAG, "onNewIntent action/type/data: ${intent.action}/${intent.type}/${intent.data}")
when (intent.action) { when {
ACTION_DIAL, ACTION_CALL, ACTION_VIEW -> isCallLogIntent(intent) -> {
handleCallLogIntent()
}
intent.action in listOf(ACTION_DIAL, ACTION_CALL, ACTION_VIEW) -> {
callAction( callAction(
applicationContext, applicationContext,
viewModel, viewModel,
intent.data, intent.data,
if (intent.action == ACTION_CALL) "call" else "dial" if (intent.action == ACTION_CALL) "call" else "dial"
) )
}
else -> { else -> {
val action = intent.getStringExtra("action") val action = intent.getStringExtra("action")
if (action != null) { if (action != null) {
@ -400,6 +408,19 @@ class MainActivity : ComponentActivity() {
return super.onKeyDown(keyCode, event) return super.onKeyDown(keyCode, event)
} }
private fun isCallLogIntent(intent: Intent?): Boolean {
return intent?.action == ACTION_VIEW && (intent.type == CallLog.Calls.CONTENT_TYPE ||
intent.data?.toString()?.contains("calls") == true)
}
private fun handleCallLogIntent() {
Log.d(TAG, "Handling Call Log intent")
val ua = BaresipService.uas.value.find { it.account.isMobile }
?: BaresipService.uas.value.firstOrNull()
if (ua != null)
viewModel.navigateToCalls(ua.account.aor)
}
private fun quitRestart(reStart: Boolean) { private fun quitRestart(reStart: Boolean) {
Log.i(TAG, "quitRestart Restart = $reStart") Log.i(TAG, "quitRestart Restart = $reStart")
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,