More work on missed mobile calls
This commit is contained in:
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
|
|||||||
@ -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),
|
||||||
|
|||||||
@ -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)!!
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user