diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 86cd169c..0c413901 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -94,6 +94,11 @@
+
+
+
+
+
{
+ 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" -> {
val remoteInputResults = RemoteInput.getResultsFromIntent(intent!!)
if (remoteInputResults != null) {
@@ -777,6 +795,14 @@ class BaresipService: Service() {
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" -> {
val uap = intent!!.getLongExtra("uap", 0L)
val uri = intent.getStringExtra("uri")!!
@@ -1560,10 +1586,16 @@ class BaresipService: Service() {
.setGroupConversation(false)
.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)
.setSmallIcon(R.drawable.ic_notification_message)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setContentIntent(pi)
+ .setDeleteIntent(dpi)
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
.setAutoCancel(true)
.setStyle(messagingStyle)
@@ -1579,9 +1611,7 @@ class BaresipService: Service() {
}
nb.setNumber(unreadMessages + 1)
- val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY)
- .setLabel(getString(R.string.reply))
- .build()
+ val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(getString(R.string.reply)).build()
val directReplyIntent = Intent(this, BaresipService::class.java)
directReplyIntent.action = "Message Inline Reply"
directReplyIntent.putExtra("uap", uap).putExtra("peer", peerUri).putExtra("time", timeStamp)
@@ -1602,10 +1632,8 @@ class BaresipService: Service() {
val saveIntent = Intent(this, BaresipService::class.java)
saveIntent.action = "Message Save"
- saveIntent.putExtra("uap", uap)
- .putExtra("time", timeStamp.toString())
- val savePendingIntent = PendingIntent
- .getService(this, SAVE_REQ_CODE, saveIntent, piFlags)
+ saveIntent.putExtra("uap", uap).putExtra("time", timeStamp.toString())
+ val savePendingIntent = PendingIntent.getService(this, SAVE_REQ_CODE, saveIntent, piFlags)
val saveAction = NotificationCompat.Action.Builder(
R.drawable.ic_notification_save,
getString(R.string.save),
@@ -1614,10 +1642,8 @@ class BaresipService: Service() {
val deleteIntent = Intent(this, BaresipService::class.java)
deleteIntent.action = "Message Delete"
- deleteIntent.putExtra("uap", uap)
- .putExtra("time", timeStamp.toString())
- val deletePendingIntent = PendingIntent
- .getService(this, DELETE_REQ_CODE, deleteIntent, piFlags)
+ deleteIntent.putExtra("uap", uap).putExtra("time", timeStamp.toString())
+ val deletePendingIntent = PendingIntent.getService(this, DELETE_REQ_CODE, deleteIntent, piFlags)
val deleteAction = NotificationCompat.Action.Builder(
R.drawable.ic_notification_delete,
getString(R.string.delete),
@@ -2250,7 +2276,8 @@ class BaresipService: Service() {
history.add()
if (call.dir == "in" && call.startTime == null && !call.rejected) {
call.ua.account.missedCalls = true
- showMissedCallNotification(uap, call.peerUri, call.ua.account.aor)
+ if (!call.ua.account.isMobile)
+ showMissedCallNotification(uap, call.peerUri, call.ua.account.aor)
}
}
synchronized(calls) {
@@ -3080,10 +3107,17 @@ class BaresipService: Service() {
intent,
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)
nb.setSmallIcon(R.drawable.ic_notification_call_missed)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setContentIntent(pi)
+ .setDeleteIntent(dpi)
.setCategory(NotificationCompat.CATEGORY_MISSED_CALL)
.setAutoCancel(true)
diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt
index e881cb47..3a9c161d 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/CallsScreen.kt
@@ -114,8 +114,13 @@ private fun CallsScreen(navController: NavController, viewModel: ViewModel, aor:
}
}
+ val ctx = LocalContext.current
+
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()
}
@@ -182,9 +187,13 @@ private fun TopAppBar(navController: NavController, ua: UserAgent, callHistory:
),
windowInsets = WindowInsets(0, 0, 0, 0),
navigationIcon = {
+ val ctx = LocalContext.current
IconButton(
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()
}
) {
@@ -495,7 +504,7 @@ private fun Calls(
count++
}
if (count > 3)
- Text("...", color = MaterialTheme.colorScheme.onBackground)
+ Text(ctx.getString(R.string.dots), color = MaterialTheme.colorScheme.onBackground)
Text(
text = Utils.friendlyUri(ctx, peerUri, ua.account),
modifier = Modifier.padding(start = 8.dp),
diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt
index bb3c9cdf..fdab48cc 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/ChatScreen.kt
@@ -1,8 +1,8 @@
package com.tutpro.baresip
-import android.content.Context
import android.content.Intent
import android.text.format.DateUtils.isToday
+import android.util.Log
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -102,7 +102,6 @@ fun NavGraphBuilder.chatScreenRoute(navController: NavController, viewModel: Vie
val aor = backStackEntry.arguments?.getString("aor")!!
val peerUri = backStackEntry.arguments?.getString("peer")!!
ChatScreen(
- ctx = LocalContext.current,
navController = navController,
viewModel = viewModel,
account = Account.ofAor(aor)!!,
@@ -113,13 +112,13 @@ fun NavGraphBuilder.chatScreenRoute(navController: NavController, viewModel: Vie
@Composable
private fun ChatScreen(
- ctx: Context,
navController: NavController,
viewModel: ViewModel,
account: Account,
peerUri: String
) {
val lifecycleOwner = LocalLifecycleOwner.current
+ val ctx = LocalContext.current
val aor = account.aor
@@ -164,6 +163,11 @@ private fun ChatScreen(
}
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)
}
@@ -173,12 +177,11 @@ private fun ChatScreen(
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
Spacer(Modifier.statusBarsPadding())
- TopAppBar(ctx, navController, viewModel, account, peerUri)
+ TopAppBar(navController, viewModel, account, peerUri)
}
},
bottomBar = {
NewMessage(
- ctx = ctx,
viewModel,
account = account,
peerUri = peerUri,
@@ -188,7 +191,6 @@ private fun ChatScreen(
content = { contentPadding ->
if (areMessagesLoaded)
ChatContent(
- ctx,
contentPadding,
account, peerUri,
chatMessages,
@@ -201,12 +203,12 @@ private fun ChatScreen(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopAppBar(
- ctx: Context,
navController: NavController,
viewModel: ViewModel,
account: Account,
peerUri: String
) {
+ val ctx = LocalContext.current
val aor = account.aor
TopAppBar(
title = {
@@ -223,7 +225,17 @@ private fun TopAppBar(
actionIconContentColor = MaterialTheme.colorScheme.onPrimary
),
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")
}
},
@@ -282,7 +294,6 @@ private fun TopAppBar(
@Composable
private fun ChatContent(
- ctx: Context,
contentPadding: PaddingValues,
account: Account,
peerUri: String,
@@ -293,14 +304,15 @@ private fun ChatContent(
modifier = Modifier.fillMaxWidth().padding(contentPadding),
verticalArrangement = Arrangement.Bottom
) {
- Account(ctx, account)
+ Account(account)
Spacer(modifier = Modifier.weight(1f))
- Messages(ctx, account, peerUri, messages, onMessageDeleted)
+ Messages(account, peerUri, messages, onMessageDeleted)
}
}
@Composable
-private fun Account(ctx: Context, account: Account) {
+private fun Account(account: Account) {
+ val ctx = LocalContext.current
Text(
text = ctx.getString(R.string.account) + " " + account.text(),
modifier = Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 8.dp),
@@ -312,12 +324,12 @@ private fun Account(ctx: Context, account: Account) {
@Composable
private fun Messages(
- ctx: Context,
account: Account,
peerUri: String,
messages: List,
onMessageDeleted: () -> Unit
) {
+ val ctx = LocalContext.current
val peerName = Utils.friendlyUri(ctx, peerUri, account)
val showDialog = remember { mutableStateOf(false) }
@@ -444,13 +456,12 @@ private fun Messages(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun NewMessage(
- ctx: Context,
viewModel: ViewModel,
account: Account,
peerUri: String,
addMessage: (message: Message) -> Unit
) {
-
+ val ctx = LocalContext.current
val aor = account.aor
val ua = UserAgent.ofAor(aor)!!
diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt
index e3be9282..d7145fa6 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/ChatsScreen.kt
@@ -1,6 +1,7 @@
package com.tutpro.baresip
-import android.content.Context
+import android.content.Intent
+import androidx.activity.compose.BackHandler
import android.text.format.DateUtils.isToday
import androidx.compose.animation.animateContentSize
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.Spacer
import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
@@ -129,8 +128,15 @@ private fun ChatsScreen(navController: NavController, aor: String) {
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(
- modifier = Modifier.fillMaxSize().imePadding(),
containerColor = MaterialTheme.colorScheme.background,
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
@@ -138,10 +144,10 @@ private fun ChatsScreen(navController: NavController, aor: String) {
TopAppBar(navController, account, uaMessages)
}
},
- bottomBar = { NewChatPeer(ctx, navController, account) },
+ bottomBar = { NewChatPeer(navController, account) },
content = { contentPadding ->
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
),
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)
}
},
@@ -214,7 +229,6 @@ private fun TopAppBar(
@Composable
private fun ChatsContent(
- ctx: Context,
navController: NavController,
contentPadding: PaddingValues,
account: Account,
@@ -225,7 +239,7 @@ private fun ChatsContent(
verticalArrangement = Arrangement.Top
) {
Account(account)
- Chats(ctx, navController, account, uaMessages)
+ Chats(navController, account, uaMessages)
}
}
@@ -242,11 +256,11 @@ private fun Account(account: Account) {
@Composable
private fun Chats(
- ctx: Context,
navController: NavController,
account: Account,
uaMessages: MutableState>
) {
+ val ctx = LocalContext.current
val aor = account.aor
val showDialog = remember { mutableStateOf(false) }
@@ -409,13 +423,14 @@ private fun Chats(
}
@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 alertMessage = remember { mutableStateOf("") }
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))
"tel:$chatPeer"
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)
showAlert.value = true
} else {
- makeChat(ctx, navController, account, peerText)
+ makeChat(navController, account, peerText)
}
}
else if (uris.size == 1)
- makeChat(ctx, navController, account, uris[0].uri)
+ makeChat(navController, account, uris[0].uri)
else {
items.value = uris.map { it.label.ifEmpty { it.uri.substringAfter(":") } }
itemAction.value = { index ->
- makeChat(ctx, navController, account, uris[index].uri)
+ makeChat(navController, account, uris[index].uri)
}
showDialog.value = true
}
diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt
index 03c57169..c07f79ff 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt
@@ -20,6 +20,7 @@ import android.content.IntentFilter
import android.media.AudioManager
import android.os.Build
import android.os.Bundle
+import android.provider.CallLog
import android.view.KeyEvent
import android.view.WindowManager
import androidx.activity.ComponentActivity
@@ -299,13 +300,21 @@ class MainActivity : ComponentActivity() {
override fun 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")
if (action != null) {
// MainActivity was not visible when call, message, or transfer request came in
intent.removeExtra("action")
handleIntent(applicationContext, viewModel, intent, action)
}
+ else if (intent.action == ACTION_VIEW && (intent.type == CallLog.Calls.CONTENT_TYPE ||
+ intent.data?.toString()?.contains("calls") == true)) {
+ Log.d(TAG, "onStart: 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)
+ }
else if (intent.action == Intent.ACTION_MAIN && !atStartup) {
val activeNotifications = nm.activeNotifications
if (activeNotifications.any { it.id == CALL_MISSED_NOTIFICATION_ID }) {
@@ -361,16 +370,25 @@ class MainActivity : ComponentActivity() {
this.setShowWhenLocked(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) {
- ACTION_DIAL, ACTION_CALL, ACTION_VIEW ->
+ when {
+ intent.action == ACTION_VIEW && (intent.type == CallLog.Calls.CONTENT_TYPE ||
+ intent.data?.toString()?.contains("calls") == true) -> {
+ Log.d(TAG, "onNewIntent: 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)
+ }
+ intent.action in listOf(ACTION_DIAL, ACTION_CALL, ACTION_VIEW) -> {
callAction(
applicationContext,
viewModel,
intent.data,
if (intent.action == ACTION_CALL) "call" else "dial"
)
+ }
else -> {
val action = intent.getStringExtra("action")
if (action != null) {
diff --git a/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt b/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt
index a8d59850..a59ae4fd 100644
--- a/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt
+++ b/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt
@@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
@@ -79,7 +80,7 @@ class ViewModel: ViewModel() {
private val _hideKeyboard = MutableStateFlow(0)
val hideKeyboard = _hideKeyboard.asStateFlow()
- private val _navigationCommand = MutableSharedFlow()
+ private val _navigationCommand = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
val navigationCommand = _navigationCommand.asSharedFlow()
private var lastRenderedAor = ""