diff --git a/CHANGELOG.md b/CHANGELOG.md
index 34bb72c3a..6f8618f16 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ Group changes to describe their impact on the project, as follows:
## [6.1.0] - Unreleased
### Added
+- Added toggle speaker action in active call notification
- Added a vu meter for recording & playback volumes (disabled by default, must be enabled in CorePreferences)
- Added a setting for user to choose whether to sort contacts by first name or last name
- Added a setting to hide contacts that have neither a SIP address nor a phone number
diff --git a/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt
index ef9ff0566..7b98ba838 100644
--- a/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt
+++ b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt
@@ -26,8 +26,10 @@ import android.content.Context
import android.content.Intent
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Address
+import org.linphone.core.AudioDevice
import org.linphone.core.ConferenceParams
import org.linphone.core.tools.Log
+import org.linphone.utils.AudioUtils
class NotificationBroadcastReceiver : BroadcastReceiver() {
companion object {
@@ -36,47 +38,69 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val notificationId = intent.getIntExtra(NotificationsManager.INTENT_NOTIF_ID, 0)
- Log.i(
- "$TAG Got notification broadcast for ID [$notificationId]"
- )
+ val action = intent.action
+ Log.i("$TAG Got notification broadcast for ID [$notificationId] with action [$action]")
// Wait for coreContext to be ready to handle intent
while (!coreContext.isReady()) {
Thread.sleep(50)
}
- if (intent.action == NotificationsManager.INTENT_ANSWER_CALL_NOTIF_ACTION || intent.action == NotificationsManager.INTENT_HANGUP_CALL_NOTIF_ACTION) {
- handleCallIntent(intent, notificationId)
- } else if (intent.action == NotificationsManager.INTENT_REPLY_MESSAGE_NOTIF_ACTION || intent.action == NotificationsManager.INTENT_MARK_MESSAGE_AS_READ_NOTIF_ACTION) {
- handleChatIntent(context, intent, notificationId)
+ if (
+ action == NotificationsManager.INTENT_ANSWER_CALL_NOTIF_ACTION ||
+ action == NotificationsManager.INTENT_HANGUP_CALL_NOTIF_ACTION ||
+ action == NotificationsManager.INTENT_TOGGLE_SPEAKER_CALL_NOTIF_ACTION
+ ) {
+ handleCallIntent(intent, notificationId, action)
+ } else if (
+ action == NotificationsManager.INTENT_REPLY_MESSAGE_NOTIF_ACTION ||
+ action == NotificationsManager.INTENT_MARK_MESSAGE_AS_READ_NOTIF_ACTION
+ ) {
+ handleChatIntent(context, intent, notificationId, action)
}
}
- private fun handleCallIntent(intent: Intent, notificationId: Int) {
- val remoteSipAddress = intent.getStringExtra(NotificationsManager.INTENT_REMOTE_ADDRESS)
- if (remoteSipAddress == null) {
+ private fun handleCallIntent(intent: Intent, notificationId: Int, action: String) {
+ val remoteSipUri = intent.getStringExtra(NotificationsManager.INTENT_REMOTE_SIP_URI)
+ if (remoteSipUri == null) {
Log.e("$TAG Remote SIP address is null for call notification ID [$notificationId]")
return
}
coreContext.postOnCoreThread { core ->
val call = core.calls.find {
- it.remoteAddress.asStringUriOnly() == remoteSipAddress
+ it.remoteAddress.asStringUriOnly() == remoteSipUri
}
if (call == null) {
- Log.e("$TAG Couldn't find call from remote address [$remoteSipAddress]")
+ Log.e("$TAG Couldn't find call from remote address [$remoteSipUri]")
} else {
- if (intent.action == NotificationsManager.INTENT_ANSWER_CALL_NOTIF_ACTION) {
- coreContext.answerCall(call)
- } else {
- coreContext.terminateCall(call)
+ when (action) {
+ NotificationsManager.INTENT_ANSWER_CALL_NOTIF_ACTION -> {
+ Log.i("$TAG Answering call with remote address [$remoteSipUri]")
+ coreContext.answerCall(call)
+ }
+ NotificationsManager.INTENT_HANGUP_CALL_NOTIF_ACTION -> {
+ Log.i("$TAG Declining/terminating call with remote address [$remoteSipUri]")
+ coreContext.terminateCall(call)
+ }
+ NotificationsManager.INTENT_TOGGLE_SPEAKER_CALL_NOTIF_ACTION -> {
+ val audioDevice = call.outputAudioDevice
+ val isUsingSpeaker = audioDevice?.type == AudioDevice.Type.Speaker
+ if (isUsingSpeaker) {
+ Log.i("$TAG Routing audio to earpiece for call [$remoteSipUri]")
+ AudioUtils.routeAudioToEarpiece(call)
+ } else {
+ Log.i("$TAG Routing audio to speaker for call [$remoteSipUri]")
+ AudioUtils.routeAudioToSpeaker(call)
+ }
+ }
}
}
}
}
- private fun handleChatIntent(context: Context, intent: Intent, notificationId: Int) {
- val remoteSipAddress = intent.getStringExtra(NotificationsManager.INTENT_REMOTE_ADDRESS)
+ private fun handleChatIntent(context: Context, intent: Intent, notificationId: Int, action: String) {
+ val remoteSipAddress = intent.getStringExtra(NotificationsManager.INTENT_REMOTE_SIP_URI)
if (remoteSipAddress == null) {
Log.e("$TAG Remote SIP address is null for notification ID [$notificationId]")
return
@@ -88,7 +112,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
}
val reply = getMessageText(intent)?.toString()
- if (intent.action == NotificationsManager.INTENT_REPLY_MESSAGE_NOTIF_ACTION) {
+ if (action == NotificationsManager.INTENT_REPLY_MESSAGE_NOTIF_ACTION) {
if (reply == null) {
Log.e("$TAG Couldn't get reply text")
return
@@ -128,13 +152,13 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
return@postOnCoreThread
}
- if (intent.action == NotificationsManager.INTENT_REPLY_MESSAGE_NOTIF_ACTION) {
+ if (action == NotificationsManager.INTENT_REPLY_MESSAGE_NOTIF_ACTION) {
val msg = room.createMessageFromUtf8(reply)
msg.userData = notificationId
msg.addListener(coreContext.notificationsManager.chatMessageListener)
msg.send()
Log.i("$TAG Reply sent for notif id [$notificationId]")
- } else if (intent.action == NotificationsManager.INTENT_MARK_MESSAGE_AS_READ_NOTIF_ACTION) {
+ } else if (action == NotificationsManager.INTENT_MARK_MESSAGE_AS_READ_NOTIF_ACTION) {
Log.i("$TAG Marking chat room from notification id [$notificationId] as read")
room.markAsRead()
if (!coreContext.notificationsManager.dismissChatNotification(room)) {
diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt
index 1e11eab28..1f5bea5f2 100644
--- a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt
+++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt
@@ -54,6 +54,7 @@ import org.linphone.contacts.ContactsManager.ContactsListener
import org.linphone.contacts.getAvatarBitmap
import org.linphone.contacts.getPerson
import org.linphone.core.Address
+import org.linphone.core.AudioDevice
import org.linphone.core.Call
import org.linphone.core.ChatMessage
import org.linphone.core.ChatMessageListener
@@ -86,13 +87,14 @@ class NotificationsManager
const val INTENT_HANGUP_CALL_NOTIF_ACTION = "org.linphone.HANGUP_CALL_ACTION"
const val INTENT_ANSWER_CALL_NOTIF_ACTION = "org.linphone.ANSWER_CALL_ACTION"
+ const val INTENT_TOGGLE_SPEAKER_CALL_NOTIF_ACTION = "org.linphone.TOGGLE_SPEAKER_CALL_ACTION"
const val INTENT_REPLY_MESSAGE_NOTIF_ACTION = "org.linphone.REPLY_ACTION"
const val INTENT_MARK_MESSAGE_AS_READ_NOTIF_ACTION = "org.linphone.MARK_AS_READ_ACTION"
const val INTENT_NOTIF_ID = "NOTIFICATION_ID"
const val KEY_TEXT_REPLY = "key_text_reply"
const val INTENT_LOCAL_IDENTITY = "LOCAL_IDENTITY"
- const val INTENT_REMOTE_ADDRESS = "REMOTE_ADDRESS"
+ const val INTENT_REMOTE_SIP_URI = "REMOTE_ADDRESS"
const val CHAT_TAG = "Chat"
private const val MISSED_CALL_TAG = "Missed call"
@@ -151,7 +153,12 @@ class NotificationsManager
Log.i(
"$TAG Found call [${addressMatch.asStringUriOnly()}] with contact in notifications, updating it"
)
- updateCallNotification(notifiable, addressMatch, friend)
+ val call = coreContext.core.getCallByRemoteAddress2(addressMatch)
+ if (call == null) {
+ Log.e("$TAG Failed to get Call from Core using remote address [${addressMatch.asStringUriOnly()}]")
+ return
+ }
+ updateCallNotification(notifiable, call, friend)
}
}
@@ -276,6 +283,18 @@ class NotificationsManager
}
}
+ @WorkerThread
+ override fun onAudioDeviceChanged(core: Core, audioDevice: AudioDevice) {
+ if (core.callsNb == 0) return
+
+ val call = core.currentCall ?: core.calls.firstOrNull()
+ if (call != null) {
+ Log.i("$TAG Audio device changed, updating call [${call.remoteAddress.asStringUriOnly()}] notification")
+ val notifiable = getNotifiableForCall(call)
+ updateCallNotification(notifiable, call, null)
+ }
+ }
+
@WorkerThread
override fun onMessagesReceived(
core: Core,
@@ -1292,22 +1311,33 @@ class NotificationsManager
setFullScreenIntent(pendingIntent, true)
}
+ if (!isIncoming) {
+ val toggleSpeakerIntent = getCallToggleSpeakerPendingIntent(notifiable)
+
+ val audioDevice = call.outputAudioDevice
+ val isUsingSpeaker = audioDevice?.type == AudioDevice.Type.Speaker
+
+ val toggleSpeakerAction = if (isUsingSpeaker) {
+ Log.i("$TAG Call is using speaker, adding action to disable it")
+ val text = AppUtils.getString(R.string.notification_disable_speaker_for_call)
+ NotificationCompat.Action.Builder(R.drawable.speaker_slash, text, toggleSpeakerIntent).build()
+ } else {
+ Log.i("$TAG Call is not using speaker, adding action to enable it")
+ val text = AppUtils.getString(R.string.notification_enable_speaker_for_call)
+ NotificationCompat.Action.Builder(R.drawable.speaker_high, text, toggleSpeakerIntent).build()
+ }
+ builder.addAction(toggleSpeakerAction)
+ }
+
return builder.build()
}
@WorkerThread
private fun updateCallNotification(
notifiable: Notifiable,
- remoteAddress: Address,
- friend: Friend
+ call: Call,
+ friend: Friend?
) {
- val call = coreContext.core.getCallByRemoteAddress2(remoteAddress)
- if (call == null) {
- Log.w(
- "$TAG Failed to find call with remote SIP URI [${remoteAddress.asStringUriOnly()}]"
- )
- return
- }
val isIncoming = LinphoneUtils.isCallIncoming(call.state)
val notification = if (isIncoming) {
@@ -1469,7 +1499,7 @@ class NotificationsManager
val hangupIntent = Intent(context, NotificationBroadcastReceiver::class.java)
hangupIntent.action = INTENT_HANGUP_CALL_NOTIF_ACTION
hangupIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId)
- hangupIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress)
+ hangupIntent.putExtra(INTENT_REMOTE_SIP_URI, notifiable.remoteAddress)
return PendingIntent.getBroadcast(
context,
@@ -1484,7 +1514,7 @@ class NotificationsManager
val answerIntent = Intent(context, NotificationBroadcastReceiver::class.java)
answerIntent.action = INTENT_ANSWER_CALL_NOTIF_ACTION
answerIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId)
- answerIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress)
+ answerIntent.putExtra(INTENT_REMOTE_SIP_URI, notifiable.remoteAddress)
return PendingIntent.getBroadcast(
context,
@@ -1494,6 +1524,21 @@ class NotificationsManager
)
}
+ @AnyThread
+ fun getCallToggleSpeakerPendingIntent(notifiable: Notifiable): PendingIntent {
+ val answerIntent = Intent(context, NotificationBroadcastReceiver::class.java)
+ answerIntent.action = INTENT_TOGGLE_SPEAKER_CALL_NOTIF_ACTION
+ answerIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId)
+ answerIntent.putExtra(INTENT_REMOTE_SIP_URI, notifiable.remoteAddress)
+
+ return PendingIntent.getBroadcast(
+ context,
+ 4,
+ answerIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ }
+
@WorkerThread
private fun displayReplyMessageNotification(message: ChatMessage, notifiable: Notifiable) {
Log.i(
@@ -1535,7 +1580,7 @@ class NotificationsManager
replyIntent.action = INTENT_REPLY_MESSAGE_NOTIF_ACTION
replyIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId)
replyIntent.putExtra(INTENT_LOCAL_IDENTITY, notifiable.localIdentity)
- replyIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress)
+ replyIntent.putExtra(INTENT_REMOTE_SIP_URI, notifiable.remoteAddress)
// PendingIntents attached to actions with remote inputs must be mutable
val replyPendingIntent = PendingIntent.getBroadcast(
@@ -1562,7 +1607,7 @@ class NotificationsManager
markAsReadIntent.action = INTENT_MARK_MESSAGE_AS_READ_NOTIF_ACTION
markAsReadIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId)
markAsReadIntent.putExtra(INTENT_LOCAL_IDENTITY, notifiable.localIdentity)
- markAsReadIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress)
+ markAsReadIntent.putExtra(INTENT_REMOTE_SIP_URI, notifiable.remoteAddress)
return PendingIntent.getBroadcast(
context,
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index bb5a546ef..9fef4ace3 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -69,6 +69,8 @@
- %s fichiers en cours de réception
Cliquez pour ouvrir
+ Activer haut-parleur
+ Désactiver haut-parleur
Bienvenue
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 42be308f7..cccb21e02 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -110,6 +110,8 @@
%s, %s
Click to open
+ Turn on speaker
+ Turn off speaker
Welcome