From 658cecc41acb9bc9fdabbe558b7e9277542c1b36 Mon Sep 17 00:00:00 2001
From: Isaac <134492608+isaacakakpo1@users.noreply.github.com>
Date: Thu, 25 Jul 2024 11:20:01 +0000
Subject: [PATCH] Release 1.3.7 (#346)
* Version Bump
* Main Stash
* Update build.gradle
* Implement WebRTC Stats
* Update WebRTC Stats to Start when enabled
* Update WebRTC Stats to Start when enabled
* Implement peer Stop
* Push Notifications Fix
* - Update ReadMe
- Refactor TelnyxClient
* - Post `telnyx_rtc.media` to livedata
- Remove parcelise as it's not supported by Java
* - Update isDev Logic
* - Refactor Code format
* - Fix UnitTest Failures
* Push Notifications Update
* Push Notifications Update
* Push Notifications Update
---
README.md | 51 +++
app/src/main/AndroidManifest.xml | 17 +-
.../main/java/com/telnyx/webrtc/sdk/App.kt | 1 +
.../telnyx/webrtc/sdk/NotificationsService.kt | 175 ++++++++
.../com/telnyx/webrtc/sdk/di/AppModule.kt | 2 +-
.../webrtc/sdk/ui/CallInstanceFragment.kt | 6 +
.../com/telnyx/webrtc/sdk/ui/MainActivity.kt | 375 +++++++++++-------
.../com/telnyx/webrtc/sdk/ui/MainViewModel.kt | 30 +-
.../sdk/utility/MyFirebaseMessagingService.kt | 94 +----
app/src/test/CredentialTest.kt | 12 +
.../main/java/com/telnyx/webrtc/sdk/Call.kt | 23 +-
.../com/telnyx/webrtc/sdk/TelnyxClient.kt | 219 +++++++++-
.../com/telnyx/webrtc/sdk/TelnyxConfig.kt | 2 +-
.../telnyx/webrtc/sdk/model/PushMetaData.kt | 8 +-
.../java/com/telnyx/webrtc/sdk/peer/Peer.kt | 96 ++++-
.../com/telnyx/webrtc/sdk/socket/TxSocket.kt | 9 +-
.../sdk/verto/receive/ReceivedResult.kt | 71 +---
.../sdk/verto/receive/SocketResponse.kt | 10 +-
.../webrtc/sdk/verto/send/ParamRequest.kt | 26 ++
.../com/telnyx/webrtc/sdk/TelnyxClientTest.kt | 20 +-
.../webrtc/sdk/testhelpers/TestConstants.kt | 2 +-
21 files changed, 903 insertions(+), 346 deletions(-)
create mode 100644 app/src/main/java/com/telnyx/webrtc/sdk/NotificationsService.kt
create mode 100644 app/src/test/CredentialTest.kt
diff --git a/README.md b/README.md
index fa9efe85..750eded8 100644
--- a/README.md
+++ b/README.md
@@ -174,6 +174,14 @@ We can then use this method to create a listener that listens for an invitation
SocketMethod.BYE.methodName -> {
// Handle a call rejection or ending - Update UI or Navigate to new screen, etc.
}
+ SocketMethod.RINGING.methodName -> {
+ // Client Can simulate ringing state
+ }
+
+ SocketMethod.RINGING.methodName -> {
+ // Ringback tone is streamed to the caller
+ // early Media - Client Can simulate ringing state
+ }
}
}
@@ -239,6 +247,49 @@ The `txPushMetaData` is neccessary for push notifications to work.
For a detailed tutorial, please visit our official [Push Notification Docs](https://developers.telnyx.com/docs/voice/webrtc/push-notifications)
+## Best Practices
+
+ 1. Handling Push Notifications : In order to properly handle push notifications, we recommend using a call type (Foreground Service)[https://developer.android.com/develop/background-work/services/foreground-services]
+ with broadcast receiver to show push notifications. An answer or reject call intent with `telnyxPushMetaData` can then be passed to the MainActivity for processing.
+ - Play a ringtone when a call is received from push notification using the `RingtoneManager`
+ ``` kotlin
+ val notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
+ RingtoneManager.getRingtone(applicationContext, notification).play()
+ ```
+ - Make Sure to set these flags for your pendingIntents, so the values get updated anytime when the notification is clicked
+ ``` kotlin
+ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ ```
+
+ ### Android 14 Requirements
+ In order to receive push notifications on Android 14, you will need to add the following permissions to your AndroidManifest.xml file and request a few at runtime:
+ ``` xml
+ // Request this permission at runtime
+
+
+ // If you need to use foreground services, you will need to add the following permissions
+
+
+
+ // Configure foregroundservice and set the foreground service type
+ // Remember to stopForegroundService when the call is answered or rejected
+
+ ```
+ 2. Handling Multiple Calls : The Telnyx WebRTC SDK allows for multiple calls to be handled at once.
+ You can use the callId to differentiate the calls.
+ ``` kotlin
+ import java.util.UUID
+ // Retrieve all calls from the TelnyxClient
+ val calls: Map = telnyxClient.calls
+
+ // Retrieve a specific call by callId
+ val currentCall: Call? = calls[callId]
+ ```
+
+
## ProGuard changes
NOTE:
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5c294edf..2f17fecc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,8 @@
+
+
@@ -13,6 +15,9 @@
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/telnyx/webrtc/sdk/App.kt b/app/src/main/java/com/telnyx/webrtc/sdk/App.kt
index 555c6ad6..6d22b375 100644
--- a/app/src/main/java/com/telnyx/webrtc/sdk/App.kt
+++ b/app/src/main/java/com/telnyx/webrtc/sdk/App.kt
@@ -5,6 +5,7 @@
package com.telnyx.webrtc.sdk
import android.app.Application
+import android.content.Context
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber
diff --git a/app/src/main/java/com/telnyx/webrtc/sdk/NotificationsService.kt b/app/src/main/java/com/telnyx/webrtc/sdk/NotificationsService.kt
new file mode 100644
index 00000000..894c90b2
--- /dev/null
+++ b/app/src/main/java/com/telnyx/webrtc/sdk/NotificationsService.kt
@@ -0,0 +1,175 @@
+package com.telnyx.webrtc.sdk
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ServiceInfo
+import android.content.res.Resources.NotFoundException
+import android.graphics.Color
+import android.media.Ringtone
+import android.media.RingtoneManager
+import android.net.Uri
+import android.os.Build
+import android.os.IBinder
+import androidx.core.app.NotificationCompat
+import com.google.gson.Gson
+import com.telnyx.webrtc.sdk.di.AppModule
+import com.telnyx.webrtc.sdk.model.PushMetaData
+import com.telnyx.webrtc.sdk.ui.MainActivity
+import com.telnyx.webrtc.sdk.utility.MyFirebaseMessagingService
+import timber.log.Timber
+
+
+class NotificationsService : Service() {
+
+
+ companion object {
+ private const val CHANNEL_ID = "PHONE_CALL_NOTIFICATION_CHANNEL"
+ private const val NOTIFICATION_ID = 1
+ const val STOP_ACTION = "STOP_ACTION"
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ createNotificationChannel()
+ }
+ private var ringtone:Ringtone? = null
+
+ private fun playPushRingTone() {
+ try {
+ val notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
+ ringtone = RingtoneManager.getRingtone(applicationContext, notification)
+ ringtone?.play()
+ } catch (e: NotFoundException) {
+ Timber.e("playPushRingTone: $e")
+ }
+ }
+
+
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+
+ val stopAction = intent?.action
+ if (stopAction != null && stopAction == STOP_ACTION) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ ringtone?.stop()
+ } else {
+ stopForeground(true)
+ }
+ return START_NOT_STICKY
+ }
+
+ val metadata = intent?.getStringExtra("metadata")
+ val telnyxPushMetadata = Gson().fromJson(metadata, PushMetaData::class.java)
+ telnyxPushMetadata?.let {
+ showNotification(it)
+ playPushRingTone()
+
+ }
+ return START_STICKY
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+
+ private fun createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val name = "Phone Call Notifications"
+ val description = "Notifications for incoming phone calls"
+ val importance = NotificationManager.IMPORTANCE_HIGH
+ val channel = NotificationChannel(CHANNEL_ID, name, importance)
+ channel.description = description
+
+ val notificationManager = getSystemService(NotificationManager::class.java)
+ channel.apply {
+ lightColor = Color.RED
+ enableLights(true)
+ enableVibration(true)
+ setSound(null, null)
+ }
+ notificationManager.createNotificationChannel(channel)
+ }
+ }
+
+ private fun showNotification(txPushMetaData: PushMetaData) {
+ val intent = Intent(this, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+ val pendingIntent: PendingIntent =
+ PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
+
+ val customSoundUri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
+
+ val rejectResultIntent = Intent(this, MainActivity::class.java)
+ rejectResultIntent.action = Intent.ACTION_VIEW
+ rejectResultIntent.putExtra(
+ MyFirebaseMessagingService.EXT_KEY_DO_ACTION,
+ MyFirebaseMessagingService.ACT_REJECT_CALL
+ )
+ rejectResultIntent.putExtra(
+ MyFirebaseMessagingService.TX_PUSH_METADATA,
+ txPushMetaData.toJson()
+ )
+ val rejectPendingIntent = PendingIntent.getActivity(
+ this,
+ MyFirebaseMessagingService.REJECT_REQUEST_CODE,
+ rejectResultIntent,
+ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+
+ val answerResultIntent = Intent(this, MainActivity::class.java)
+ answerResultIntent.setAction(Intent.ACTION_VIEW)
+
+ answerResultIntent.putExtra(
+ MyFirebaseMessagingService.EXT_KEY_DO_ACTION,
+ MyFirebaseMessagingService.ACT_ANSWER_CALL
+ )
+
+ answerResultIntent.putExtra(
+ MyFirebaseMessagingService.TX_PUSH_METADATA,
+ txPushMetaData.toJson()
+ )
+
+ val answerPendingIntent = PendingIntent.getActivity(
+ this,
+ MyFirebaseMessagingService.ANSWER_REQUEST_CODE,
+ answerResultIntent,
+ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ Timber.d("showNotification: ${txPushMetaData.toJson()}")
+
+
+ val builder = NotificationCompat.Builder(this, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_stat_contact_phone)
+ .setContentTitle("Incoming Call")
+ .setContentText("Incoming call from: ")
+ .setPriority(NotificationCompat.PRIORITY_MAX)
+ .setContentIntent(pendingIntent)
+ .setSound(customSoundUri)
+ .addAction(
+ R.drawable.ic_call_white,
+ MyFirebaseMessagingService.ACT_ANSWER_CALL, answerPendingIntent
+ )
+ .addAction(
+ R.drawable.ic_call_end_white,
+ MyFirebaseMessagingService.ACT_REJECT_CALL, rejectPendingIntent
+ )
+ .setOngoing(true)
+ .setAutoCancel(false)
+ .setCategory(NotificationCompat.CATEGORY_CALL)
+ .setFullScreenIntent(pendingIntent, true)
+
+ startForeground(
+ NOTIFICATION_ID,
+ builder.build(),
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
+ )
+ }
+
+
+}
diff --git a/app/src/main/java/com/telnyx/webrtc/sdk/di/AppModule.kt b/app/src/main/java/com/telnyx/webrtc/sdk/di/AppModule.kt
index 14f96efb..b046a268 100644
--- a/app/src/main/java/com/telnyx/webrtc/sdk/di/AppModule.kt
+++ b/app/src/main/java/com/telnyx/webrtc/sdk/di/AppModule.kt
@@ -17,7 +17,7 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object AppModule {
- private const val SHARED_PREFERENCES_KEY = "TelnyxSharedPreferences"
+ const val SHARED_PREFERENCES_KEY = "TelnyxSharedPreferences"
@Singleton
@Provides
diff --git a/app/src/main/java/com/telnyx/webrtc/sdk/ui/CallInstanceFragment.kt b/app/src/main/java/com/telnyx/webrtc/sdk/ui/CallInstanceFragment.kt
index ee06e764..9682007c 100644
--- a/app/src/main/java/com/telnyx/webrtc/sdk/ui/CallInstanceFragment.kt
+++ b/app/src/main/java/com/telnyx/webrtc/sdk/ui/CallInstanceFragment.kt
@@ -146,6 +146,12 @@ class CallInstanceFragment : Fragment(), NumberKeyboardListener {
when (data?.method) {
SocketMethod.INVITE.methodName -> {
//NOOP
+ }
+ SocketMethod.RINGING.methodName -> {
+
+ }
+ SocketMethod.MEDIA.methodName -> {
+
}
SocketMethod.BYE.methodName -> {
diff --git a/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainActivity.kt b/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainActivity.kt
index 84a2b479..2a74a2c7 100644
--- a/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainActivity.kt
+++ b/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainActivity.kt
@@ -9,9 +9,14 @@ import android.Manifest.permission.INTERNET
import android.Manifest.permission.RECORD_AUDIO
import android.app.AlertDialog
import android.app.Dialog
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
+import android.content.Intent
+import android.graphics.Color
import android.media.RingtoneManager
import android.os.Build
import android.os.Bundle
@@ -24,17 +29,20 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.google.firebase.FirebaseApp
import com.google.firebase.messaging.FirebaseMessaging
+import com.google.gson.Gson
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
+import com.telnyx.webrtc.sdk.App
import com.telnyx.webrtc.sdk.CredentialConfig
import com.telnyx.webrtc.sdk.MOCK_CALLER_NAME
import com.telnyx.webrtc.sdk.MOCK_CALLER_NUMBER
import com.telnyx.webrtc.sdk.MOCK_DESTINATION_NUMBER
import com.telnyx.webrtc.sdk.MOCK_PASSWORD
import com.telnyx.webrtc.sdk.MOCK_USERNAME
+import com.telnyx.webrtc.sdk.NotificationsService
import com.telnyx.webrtc.sdk.R
import com.telnyx.webrtc.sdk.TokenConfig
import com.telnyx.webrtc.sdk.databinding.ActivityMainBinding
@@ -42,6 +50,7 @@ import com.telnyx.webrtc.sdk.manager.UserManager
import com.telnyx.webrtc.sdk.model.AudioDevice
import com.telnyx.webrtc.sdk.model.CallState
import com.telnyx.webrtc.sdk.model.LogLevel
+import com.telnyx.webrtc.sdk.model.PushMetaData
import com.telnyx.webrtc.sdk.model.SocketMethod
import com.telnyx.webrtc.sdk.model.TxServerConfiguration
import com.telnyx.webrtc.sdk.ui.wsmessages.WsMessageFragment
@@ -77,6 +86,8 @@ class MainActivity : AppCompatActivity() {
private var isDev = false
private var isAutomaticLogin = false
private var wsMessageList: ArrayList? = null
+ private var credentialConfig: CredentialConfig? = null
+ private var tokenConfig: TokenConfig? = null
// Notification handling
private var notificationAcceptHandling: Boolean? = null
@@ -110,8 +121,10 @@ class MainActivity : AppCompatActivity() {
checkPermissions()
- handleCallNotification()
initViews()
+ handleServiceIntent(intent)
+ handleUserLoginState()
+ binding.toolbarId.setOnMenuItemClickListener(this::onOptionsItemSelected)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -121,10 +134,9 @@ class MainActivity : AppCompatActivity() {
}
-
- override fun onOptionsItemSelected(item: MenuItem):Boolean {
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
Timber.d("onOptionsItemSelected ${item.itemId}")
- return when (item.itemId) {
+ return when (item.itemId) {
R.id.action_disconnect -> {
if (userManager.isUserLogin) {
disconnectPressed()
@@ -159,6 +171,7 @@ class MainActivity : AppCompatActivity() {
}
}
}
+
private fun createAudioOutputSelectionDialog(): Dialog {
return this.let {
val audioOutputList = arrayOf("Phone", "Bluetooth", "Loud Speaker")
@@ -194,112 +207,192 @@ class MainActivity : AppCompatActivity() {
}
private fun connectToSocketAndObserve(txPushMetaData: String? = null) {
+
+ Timber.d("doLogin")
+ // path to ringtone and ringBackTone
+ val ringtone = R.raw.incoming_call
+ val ringBackTone = R.raw.ringback_tone
+
+ if (userManager.isUserLogin) {
+ val loginConfig = CredentialConfig(
+ userManager.sipUsername,
+ userManager.sipPass,
+ userManager.callerIdNumber,
+ userManager.callerIdNumber,
+ userManager.fcmToken,
+ RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE),// or ringtone,
+ R.raw.ringback_tone,
+ LogLevel.ALL
+ )
+ credentialConfig = loginConfig
+ } else {
+ binding.loginSectionId.apply {
+ if (tokenLoginSwitch.isChecked) {
+ loginTokenId.apply {
+ val sipToken = sipTokenId.text.toString()
+ val sipCallerName = tokenCallerIdNameId.text.toString()
+ val sipCallerNumber = tokenCallerIdNumberId.text.toString()
+
+ val loginConfig = TokenConfig(
+ sipToken,
+ sipCallerName,
+ sipCallerNumber,
+ fcmToken,
+ ringtone,
+ ringBackTone,
+ LogLevel.ALL
+ )
+ tokenConfig = loginConfig
+ }
+ } else {
+ loginCredentialId.apply {
+ val sipUsername = sipUsernameId.text.toString()
+ val password = sipPasswordId.text.toString()
+ val sipCallerName = callerIdNameId.text.toString()
+ val sipCallerNumber = callerIdNumberId.text.toString()
+
+ val loginConfig = CredentialConfig(
+ sipUsername,
+ password,
+ sipCallerName,
+ sipCallerNumber,
+ fcmToken,
+ RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), // or ringtone,
+ ringBackTone,
+ LogLevel.ALL
+ )
+ credentialConfig = loginConfig
+ }
+ }
+ }
+
+ }
+ Timber.d("Connect to Socket and Observe")
if (!isDev) {
- mainViewModel.initConnection(applicationContext, null, txPushMetaData)
+ mainViewModel.initConnection(
+ applicationContext,
+ null,
+ credentialConfig = credentialConfig!!,
+ tokenConfig = tokenConfig,
+ txPushMetaData
+ )
} else {
mainViewModel.initConnection(
applicationContext,
TxServerConfiguration(host = "rtcdev.telnyx.com"),
+ credentialConfig = credentialConfig,
+ tokenConfig = tokenConfig,
txPushMetaData
)
+
}
observeSocketResponses()
}
private fun observeSocketResponses() {
- mainViewModel.getSocketResponse()
- ?.observe(
- this,
- object : SocketObserver() {
- override fun onConnectionEstablished() {
- doLogin(isAutomaticLogin)
- }
+ mainViewModel.getSocketResponse()?.observe(
+ this,
+ object : SocketObserver() {
+ override fun onConnectionEstablished() {
+ Timber.d("OnConMan")
- override fun onMessageReceived(data: ReceivedMessageBody?) {
- Timber.d("onMessageReceived from SDK [%s]", data?.method)
- when (data?.method) {
- SocketMethod.CLIENT_READY.methodName -> {
- Timber.d("You are ready to make calls.")
- }
+ }
- SocketMethod.LOGIN.methodName -> {
- binding.progressIndicatorId.visibility = View.INVISIBLE
- val sessionId = (data.result as LoginResponse).sessid
- Timber.d("Current Session: $sessionId")
- onLoginSuccessfullyViews()
- }
+ override fun onMessageReceived(data: ReceivedMessageBody?) {
+ Timber.d("onMessageReceived from SDK [%s]", data?.method)
+ when (data?.method) {
+ SocketMethod.CLIENT_READY.methodName -> {
+ Timber.d("You are ready to make calls.")
- SocketMethod.INVITE.methodName -> {
- val inviteResponse = data.result as InviteResponse
- onReceiveCallView(
- inviteResponse.callId,
- inviteResponse.callerIdNumber
- )
- }
+ }
- SocketMethod.ANSWER.methodName -> {
- val callId = (data.result as AnswerResponse).callId
- launchCallInstance(callId)
- binding.apply {
- callControlSectionId.callButtonId.visibility =
- View.VISIBLE
- callControlSectionId.cancelCallButtonId.visibility =
- View.GONE
- }
-
- invitationSent = false
- }
+ SocketMethod.LOGIN.methodName -> {
+ binding.progressIndicatorId.visibility = View.INVISIBLE
+ val sessionId = (data.result as LoginResponse).sessid
+ Timber.d("Current Session: $sessionId")
+ onLoginSuccessfullyViews()
+ }
- SocketMethod.BYE.methodName -> {
- onByeReceivedViews()
- val callId = (data.result as ByeResponse).callId
- val callInstanceFragment = callInstanceFragments[callId]
- callInstanceFragment?.let {
- supportFragmentManager.beginTransaction().remove(it).commit()
- }
+ SocketMethod.INVITE.methodName -> {
+ val inviteResponse = data.result as InviteResponse
+ onReceiveCallView(
+ inviteResponse.callId,
+ inviteResponse.callerIdNumber
+ )
+ }
+
+ SocketMethod.ANSWER.methodName -> {
+ val callId = (data.result as AnswerResponse).callId
+ launchCallInstance(callId)
+ binding.apply {
+ callControlSectionId.callButtonId.visibility =
+ View.VISIBLE
+ callControlSectionId.cancelCallButtonId.visibility =
+ View.GONE
}
+
+ invitationSent = false
}
- }
- override fun onLoading() {
- Timber.i("Loading...")
- }
+ SocketMethod.RINGING.methodName -> {
+ // Client Can simulate ringing state
+ }
- override fun onChanged(value: SocketResponse) {
- super.onChanged(value)
- // Do Nothing
- }
+ SocketMethod.MEDIA.methodName -> {
+ // Ringback tone is streamed to the caller
+ // early Media - Client Can simulate ringing state
+ }
- override fun onError(message: String?) {
- Timber.e("onError: %s", message)
- Toast.makeText(
- this@MainActivity,
- message ?: "Socket Connection Error",
- Toast.LENGTH_SHORT
- ).show()
+ SocketMethod.BYE.methodName -> {
+ onByeReceivedViews()
+ val callId = (data.result as ByeResponse).callId
+ val callInstanceFragment = callInstanceFragments[callId]
+ callInstanceFragment?.let {
+ supportFragmentManager.beginTransaction().remove(it).commit()
+ }
+ }
}
+ }
- override fun onSocketDisconnect() {
- Toast.makeText(
- this@MainActivity,
- "Socket is disconnected",
- Toast.LENGTH_SHORT
- ).show()
-
- binding.apply {
- progressIndicatorId.visibility = View.INVISIBLE
- incomingCallView.visibility = View.GONE
- callControlView.visibility = View.GONE
- loginSectionView.visibility = View.VISIBLE
-
- socketTextValue.text = getString(R.string.disconnected)
- callStateTextValue.text = "-"
- }
+ override fun onLoading() {
+ Timber.i("Loading...")
+ }
+ override fun onChanged(value: SocketResponse) {
+ super.onChanged(value)
+ // Do Nothing
+ }
+
+ override fun onError(message: String?) {
+ Timber.e("onError: %s", message)
+ Toast.makeText(
+ this@MainActivity,
+ message ?: "Socket Connection Error",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ override fun onSocketDisconnect() {
+ Toast.makeText(
+ this@MainActivity,
+ "Socket is disconnected",
+ Toast.LENGTH_SHORT
+ ).show()
+
+ binding.apply {
+ progressIndicatorId.visibility = View.INVISIBLE
+ incomingCallView.visibility = View.GONE
+ callControlView.visibility = View.GONE
+ loginSectionView.visibility = View.VISIBLE
+
+ socketTextValue.text = getString(R.string.disconnected)
+ callStateTextValue.text = "-"
}
+
+
}
- )
+ }
+ )
}
private fun observeWsMessage() {
@@ -323,7 +416,6 @@ class MainActivity : AppCompatActivity() {
private fun initViews() {
mockInputs()
- handleUserLoginState()
getFCMToken()
observeWsMessage()
@@ -466,75 +558,17 @@ class MainActivity : AppCompatActivity() {
private fun connectButtonPressed() {
binding.progressIndicatorId.visibility = View.VISIBLE
- if (notificationAcceptHandling == true) {
- Timber.d("notificationAcceptHandling is true $txPushMetaData")
- if (txPushMetaData != null) {
- connectToSocketAndObserve(txPushMetaData)
- }
- } else {
+ Timber.d("notificationAcceptHandling is true $txPushMetaData")
+ if (txPushMetaData != null) {
+ connectToSocketAndObserve(txPushMetaData)
+ }
+ else {
connectToSocketAndObserve()
}
}
private fun doLogin(isAuto: Boolean) {
- // path to ringtone and ringBackTone
- val ringtone = R.raw.incoming_call
- val ringBackTone = R.raw.ringback_tone
-
- if (isAuto) {
- val loginConfig = CredentialConfig(
- userManager.sipUsername,
- userManager.sipPass,
- userManager.callerIdNumber,
- userManager.callerIdNumber,
- userManager.fcmToken,
- RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE),// or ringtone,
- R.raw.ringback_tone,
- LogLevel.ALL
- )
- mainViewModel.doLoginWithCredentials(loginConfig)
- } else {
- binding.loginSectionId.apply {
- if (tokenLoginSwitch.isChecked) {
- loginTokenId.apply {
- val sipToken = sipTokenId.text.toString()
- val sipCallerName = tokenCallerIdNameId.text.toString()
- val sipCallerNumber = tokenCallerIdNumberId.text.toString()
- val loginConfig = TokenConfig(
- sipToken,
- sipCallerName,
- sipCallerNumber,
- fcmToken,
- ringtone,
- ringBackTone,
- LogLevel.ALL
- )
- mainViewModel.doLoginWithToken(loginConfig)
- }
- } else {
- loginCredentialId.apply {
- val sipUsername = sipUsernameId.text.toString()
- val password = sipPasswordId.text.toString()
- val sipCallerName = callerIdNameId.text.toString()
- val sipCallerNumber = callerIdNumberId.text.toString()
-
- val loginConfig = CredentialConfig(
- sipUsername,
- password,
- sipCallerName,
- sipCallerNumber,
- fcmToken,
- RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), // or ringtone,
- ringBackTone,
- LogLevel.ALL
- )
- mainViewModel.doLoginWithCredentials(loginConfig)
- }
- }
- }
-
- }
}
private fun getFCMToken() {
@@ -655,12 +689,14 @@ class MainActivity : AppCompatActivity() {
incomingActiveCallSectionId.root.bringToFront()
incomingActiveCallSectionId.endAndAccept.setOnClickListener {
- mainViewModel.currentCall?.let {
+ mainViewModel.currentCall!!.let {
isActiveBye = true
it.endCall(it.callId)
+ }.also {
+ mainViewModel.setCurrentCall(callId)
+ onAcceptCall(callId, callerIdNumber)
}
- mainViewModel.setCurrentCall(callId)
- onAcceptCall(callId, callerIdNumber)
+
}
incomingActiveCallSectionId.rejectCurrentCall.setOnClickListener {
@@ -757,20 +793,59 @@ class MainActivity : AppCompatActivity() {
}
}
- private fun handleCallNotification() {
+ private fun handleCallNotification(intent: Intent?) {
+
+ if (intent == null) {
+ Timber.d("Intent is null")
+ return
+ }
+
+ Timber.d("onNewIntent ")
+ val serviceIntent = Intent(this, NotificationsService::class.java).apply {
+ putExtra("action", NotificationsService.STOP_ACTION)
+ }
+ serviceIntent.setAction(NotificationsService.STOP_ACTION)
+ startService(serviceIntent)
+
val action = intent.extras?.getString(MyFirebaseMessagingService.EXT_KEY_DO_ACTION)
action?.let {
txPushMetaData = intent.extras?.getString(MyFirebaseMessagingService.TX_PUSH_METADATA)
+ Timber.d("Action: $action ${txPushMetaData ?: "No Metadata"}")
if (action == MyFirebaseMessagingService.ACT_ANSWER_CALL) {
// Handle Answer
notificationAcceptHandling = true
+ Timber.d("Call answered from notification")
+
} else if (action == MyFirebaseMessagingService.ACT_REJECT_CALL) {
// Handle Reject
notificationAcceptHandling = false
+ Timber.d("Call rejected from notification")
}
+ connectButtonPressed()
}
}
+ override fun onResume() {
+ super.onResume()
+ Timber.d("onResume")
+ }
+
+ override fun onStop() {
+ super.onStop()
+ Timber.d("onStop")
+ disconnectPressed()
+ }
+
+ override fun onNewIntent(intent: Intent?) {
+ super.onNewIntent(intent)
+ handleServiceIntent(intent)
+ }
+
+ private fun handleServiceIntent(intent: Intent?) {
+
+ handleCallNotification(intent)
+ }
+
}
diff --git a/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainViewModel.kt b/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainViewModel.kt
index af20fab2..3dac62b1 100644
--- a/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainViewModel.kt
+++ b/app/src/main/java/com/telnyx/webrtc/sdk/ui/MainViewModel.kt
@@ -40,16 +40,36 @@ class MainViewModel @Inject constructor(
fun initConnection(
context: Context,
providedServerConfig: TxServerConfiguration?,
+ credentialConfig: CredentialConfig?,
+ tokenConfig: TokenConfig?,
txPushMetaData: String?
) {
+ Timber.e("initConnection")
telnyxClient = TelnyxClient(context)
+
providedServerConfig?.let {
- telnyxClient?.connect(it, txPushMetaData)
+ telnyxClient?.connect(it, credentialConfig!!, txPushMetaData, true)
} ?: run {
- telnyxClient?.connect(txPushMetaData = txPushMetaData)
+ if (tokenConfig != null) {
+ telnyxClient?.connect(
+ txPushMetaData = txPushMetaData,
+ tokenConfig = tokenConfig,
+ autoLogin = true
+ )
+ } else {
+ telnyxClient?.connect(
+ txPushMetaData = txPushMetaData,
+ credentialConfig = credentialConfig!!,
+ autoLogin = true
+ )
+ }
}
}
+ fun startDebugStats() {
+ currentCall?.startDebug()
+ }
+
fun saveUserData(
userName: String,
password: String,
@@ -89,10 +109,6 @@ class MainViewModel @Inject constructor(
fun getIsOnHoldStatus(): LiveData? = currentCall?.getIsOnHoldStatus()
fun getIsOnLoudSpeakerStatus(): LiveData? = currentCall?.getIsOnLoudSpeakerStatus()
- fun doLoginWithCredentials(credentialConfig: CredentialConfig) {
- telnyxClient?.credentialLogin(credentialConfig)
- Timber.e("token_ ${credentialConfig.fcmToken}")
- }
fun doLoginWithToken(tokenConfig: TokenConfig) {
telnyxClient?.tokenLogin(tokenConfig)
@@ -104,7 +120,7 @@ class MainViewModel @Inject constructor(
destinationNumber: String,
clientState: String
) {
- val call = telnyxClient?.newInvite(
+ val call = telnyxClient?.newInvite(
callerName, callerNumber, destinationNumber,
clientState, mapOf(Pair("X-test", "123456"))
)
diff --git a/app/src/main/java/com/telnyx/webrtc/sdk/utility/MyFirebaseMessagingService.kt b/app/src/main/java/com/telnyx/webrtc/sdk/utility/MyFirebaseMessagingService.kt
index 8c6759b4..0eb18462 100644
--- a/app/src/main/java/com/telnyx/webrtc/sdk/utility/MyFirebaseMessagingService.kt
+++ b/app/src/main/java/com/telnyx/webrtc/sdk/utility/MyFirebaseMessagingService.kt
@@ -4,25 +4,13 @@
package com.telnyx.webrtc.sdk.utility
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
import android.content.Intent
-import android.graphics.Color
-import android.media.RingtoneManager
-import android.os.Build
-import androidx.annotation.RequiresApi
-import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.google.gson.Gson
-import com.telnyx.webrtc.sdk.R
-import com.telnyx.webrtc.sdk.model.PushMetaData
-import com.telnyx.webrtc.sdk.ui.MainActivity
+import com.telnyx.webrtc.sdk.NotificationsService
import org.json.JSONObject
import timber.log.Timber
-import java.util.*
class MyFirebaseMessagingService : FirebaseMessagingService() {
@@ -35,83 +23,19 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
Timber.d("Message Received From Firebase: ${remoteMessage.data}")
+ Timber.d("Message Received From Firebase Priority: ${remoteMessage.priority}")
+ Timber.d("Message Received From Firebase: ${remoteMessage.originalPriority}")
val params = remoteMessage.data
val objects = JSONObject(params as Map<*, *>)
val metadata = objects.getString("metadata")
- val gson = Gson()
- val telnyxPushMetadata = gson.fromJson(metadata, PushMetaData::class.java)
-
- val notificationManager =
- getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- val notificationID = Random().nextInt(3000)
-
- /*
- Apps targeting SDK 26 or above (Android O) must implement notification channels and add its notifications
- to at least one of them.
- */
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- setupChannels(notificationManager)
+ val serviceIntent = Intent(this, NotificationsService::class.java).apply {
+ putExtra("metadata", metadata)
}
-
- val rejectResultIntent = Intent(this, MainActivity::class.java)
- rejectResultIntent.addCategory(Intent.CATEGORY_LAUNCHER)
- rejectResultIntent.action = Intent.ACTION_VIEW
- rejectResultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
- rejectResultIntent.putExtra(EXT_KEY_DO_ACTION, ACT_REJECT_CALL)
- val rejectPendingIntent = PendingIntent.getActivity(
- this,
- REJECT_REQUEST_CODE,
- rejectResultIntent,
- PendingIntent.FLAG_IMMUTABLE
- )
-
- val answerResultIntent = Intent(this, MainActivity::class.java)
- answerResultIntent.addCategory(Intent.CATEGORY_LAUNCHER)
- answerResultIntent.action = Intent.ACTION_VIEW
- answerResultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
- answerResultIntent.putExtra(EXT_KEY_DO_ACTION, ACT_ANSWER_CALL)
-
- answerResultIntent.putExtra(TX_PUSH_METADATA, metadata)
-
- val answerPendingIntent = PendingIntent.getActivity(
- this,
- ANSWER_REQUEST_CODE,
- answerResultIntent,
- PendingIntent.FLAG_IMMUTABLE
- )
-
- val notificationSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
- val notificationBuilder = NotificationCompat.Builder(this, TELNYX_CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_stat_contact_phone)
- .setPriority(NotificationCompat.PRIORITY_MAX)
- .setContentTitle(remoteMessage.data["title"])
- .setContentText(telnyxPushMetadata.callerName + " - " + telnyxPushMetadata.callerNumber)
- .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
- .addAction(R.drawable.ic_call_white, ACT_ANSWER_CALL, answerPendingIntent)
- .addAction(R.drawable.ic_call_end_white, ACT_REJECT_CALL, rejectPendingIntent)
- .setAutoCancel(true)
- .setSound(notificationSoundUri)
-
- notificationManager.notify(notificationID, notificationBuilder.build())
+ startForegroundService(serviceIntent)
}
- @RequiresApi(api = Build.VERSION_CODES.O)
- private fun setupChannels(notificationManager: NotificationManager?) {
- val adminChannelName = "New notification"
- val adminChannelDescription = "Device to device notification"
- val adminChannel = NotificationChannel(
- TELNYX_CHANNEL_ID,
- adminChannelName,
- NotificationManager.IMPORTANCE_HIGH
- )
- adminChannel.description = adminChannelDescription
- adminChannel.enableLights(true)
- adminChannel.lightColor = Color.RED
- adminChannel.enableVibration(true)
- notificationManager?.createNotificationChannel(adminChannel)
- }
/**
* Called if InstanceID token is updated. This may occur if the security of
@@ -142,9 +66,9 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
companion object {
private const val TAG = "MyFirebaseMsgService"
- private const val TELNYX_CHANNEL_ID = "telnyx_channel"
- private const val ANSWER_REQUEST_CODE = 0
- private const val REJECT_REQUEST_CODE = 1
+ const val TELNYX_CHANNEL_ID = "telnyx_channel"
+ const val ANSWER_REQUEST_CODE = 0
+ const val REJECT_REQUEST_CODE = 1
const val TX_PUSH_METADATA = "tx_push_metadata"
diff --git a/app/src/test/CredentialTest.kt b/app/src/test/CredentialTest.kt
new file mode 100644
index 00000000..3bbc54e9
--- /dev/null
+++ b/app/src/test/CredentialTest.kt
@@ -0,0 +1,12 @@
+import com.telnyx.webrtc.sdk.MOCK_PASSWORD
+import com.telnyx.webrtc.sdk.MOCK_USERNAME
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class CredentialTest {
+ @Test
+ fun testCredential() {
+ assertEquals(MOCK_USERNAME, "")
+ assertEquals(MOCK_PASSWORD, "")
+ }
+}
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/Call.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/Call.kt
index 3e05dd4c..153a66b2 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/Call.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/Call.kt
@@ -41,23 +41,22 @@ import kotlin.concurrent.timerTask
*/
data class CustomHeaders(val name: String, val value: String)
-
-data class Call(
+ data class Call(
val context: Context,
val client: TelnyxClient,
var socket: TxSocket,
val sessionId: String,
val audioManager: AudioManager,
val providedTurn: String = Config.DEFAULT_TURN,
- val providedStun: String = Config.DEFAULT_STUN
-) {
+ val providedStun: String = Config.DEFAULT_STUN,
+ ) {
companion object {
const val ICE_CANDIDATE_DELAY: Long = 400
}
-
internal var peerConnection: Peer? = null
+
internal var earlySDP = false
var inviteResponse:InviteResponse? = null
@@ -86,6 +85,19 @@ data class Call(
loudSpeakerLiveData.postValue(audioManager.isSpeakerphoneOn)
}
+ fun startDebug(){
+ Timber.d("Peer connection debug started")
+
+ peerConnection?.startTimer()
+ }
+
+ fun stopDebug(){
+ Timber.d("Peer connection debug stopped")
+ peerConnection?.stopTimer()
+ }
+
+
+
/**
* Initiates a new call invitation
* @param callerName, the name to appear on the invitation
@@ -131,6 +143,7 @@ data class Call(
customHeaders: Map? = null
) {
client.acceptCall(callId, destinationNumber, customHeaders)
+
}
/**
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxClient.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxClient.kt
index 768bca7f..b0f5bfc3 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxClient.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxClient.kt
@@ -61,6 +61,7 @@ class TelnyxClient(
const val RETRY_REGISTER_TIME = 3
const val RETRY_CONNECT_TIME = 3
const val GATEWAY_RESPONSE_DELAY: Long = 3000
+
}
private var credentialSessionConfig: CredentialConfig? = null
@@ -83,11 +84,13 @@ class TelnyxClient(
internal var providedTurn: String? = null
internal var providedStun: String? = null
+ internal var debugReportStarted = false
+
// MediaPlayer for ringtone / ringbacktone
private var mediaPlayer: MediaPlayer? = null
var sessid: String // sessid used to recover calls when reconnecting
- val socketResponseLiveData = MutableLiveData>()
+ lateinit var socketResponseLiveData: MutableLiveData>
val wsMessagesResponseLiveDate = MutableLiveData()
private val audioManager =
@@ -111,6 +114,7 @@ class TelnyxClient(
private var isCallPendingFromPush: Boolean = false
private var pushMetaData: PushMetaData? = null
private fun processCallFromPush(metaData: PushMetaData) {
+ Log.d("processCallFromPush PushMetaData", metaData.toJson())
isCallPendingFromPush = true
this.pushMetaData = metaData
}
@@ -130,7 +134,7 @@ class TelnyxClient(
sessid,
audioManager!!,
providedTurn!!,
- providedStun!!
+ providedStun!!,
)
}
} else {
@@ -150,7 +154,7 @@ class TelnyxClient(
callId: UUID,
destinationNumber: String,
customHeaders: Map? = null
- ) : Call {
+ ): Call {
val acceptCall = calls[callId]
acceptCall!!.apply {
val uuid: String = UUID.randomUUID().toString()
@@ -158,8 +162,7 @@ class TelnyxClient(
peerConnection?.getLocalDescription()?.description
if (sessionDescriptionString == null) {
callStateLiveData.postValue(CallState.ERROR)
- }
- else {
+ } else {
val answerBodyMessage = SendingMessageBody(
uuid, SocketMethod.ANSWER.methodName,
CallParams(
@@ -193,7 +196,7 @@ class TelnyxClient(
destinationNumber: String,
clientState: String,
customHeaders: Map? = null
- ) : Call {
+ ): Call {
val inviteCall = call!!.copy(
context = context,
client = this,
@@ -206,12 +209,11 @@ class TelnyxClient(
val uuid: String = UUID.randomUUID().toString()
val inviteCallId: UUID = UUID.randomUUID()
- // set global call CallID
callId = inviteCallId
// Create new peer
peerConnection = Peer(
- context, client, providedTurn, providedStun,
+ context, client, providedTurn, providedStun, callId.toString(),
object : PeerConnectionObserver() {
override fun onIceCandidate(p0: IceCandidate?) {
super.onIceCandidate(p0)
@@ -392,11 +394,12 @@ class TelnyxClient(
// Generate random UUID for sessid param, convert it to string and set globally
sessid = UUID.randomUUID().toString()
+ socketResponseLiveData =
+ MutableLiveData>(SocketResponse.initialised())
socket = TxSocket(
host_address = Config.TELNYX_PROD_HOST_ADDRESS,
port = Config.TELNYX_PORT
)
-
registerNetworkCallback()
}
@@ -427,12 +430,22 @@ class TelnyxClient(
* @param txPushMetaData, the push metadata used to connect to a call from push
* (Get this from push notification - fcm data payload)
* required fot push calls to work
+ *
*/
+ @Deprecated("this telnyxclient.connect is deprecated." +
+ " Use telnyxclient.connect(providedServerConfig,txPushMetaData," +
+ "credential or tokenLogin) instead.")
fun connect(
providedServerConfig: TxServerConfiguration = TxServerConfiguration(),
- txPushMetaData: String?
+ txPushMetaData: String? = null,
) {
+ socketResponseLiveData =
+ MutableLiveData>(SocketResponse.initialised())
+ waitingForReg = true
+ invalidateGatewayResponseTimer()
+ resetGatewayCounters()
+
providedHostAddress = if (txPushMetaData != null) {
val metadata = Gson().fromJson(txPushMetaData, PushMetaData::class.java)
processCallFromPush(metadata)
@@ -441,23 +454,122 @@ class TelnyxClient(
providedServerConfig.host
}
+ socket = TxSocket(
+ host_address = providedHostAddress!!,
+ port = providedServerConfig.port
+ )
+
+ providedPort = providedServerConfig.port
+ providedTurn = providedServerConfig.turn
+ providedStun = providedServerConfig.stun
+ if (ConnectivityHelper.isNetworkEnabled(context)) {
+ Timber.d("Provided Host Address: $providedHostAddress")
+ socket.connect(this, providedHostAddress, providedPort, pushMetaData) {
+
+ }
+ } else {
+ socketResponseLiveData.postValue(SocketResponse.error("No Network Connection"))
+ }
+ }
+
+
+ /**
+ * Connects to the socket using this client as the listener
+ * Will respond with 'No Network Connection' if there is no network available
+ * @see [TxSocket]
+ * @param providedServerConfig, the TxServerConfiguration used to connect to the socket
+ * @param txPushMetaData, the push metadata used to connect to a call from push
+ * (Get this from push notification - fcm data payload)
+ * required fot push calls to work
+ *
+ * @param autoLogin, if true, the SDK will automatically log in with
+ * the provided credentials on connection established
+ * We recommend setting this to true
+ *
+ */
+ fun connect(
+ providedServerConfig: TxServerConfiguration = TxServerConfiguration(),
+ credentialConfig: CredentialConfig,
+ txPushMetaData: String? = null,
+ autoLogin: Boolean = true,
+ ) {
+
+ socketResponseLiveData =
+ MutableLiveData>(SocketResponse.initialised())
+ waitingForReg = true
invalidateGatewayResponseTimer()
resetGatewayCounters()
+ providedHostAddress = if (txPushMetaData != null) {
+ val metadata = Gson().fromJson(txPushMetaData, PushMetaData::class.java)
+ processCallFromPush(metadata)
+ providedServerConfig.host
+ } else {
+ providedServerConfig.host
+ }
+
+ socket = TxSocket(
+ host_address = providedHostAddress!!,
+ port = providedServerConfig.port
+ )
+
+ providedPort = providedServerConfig.port
+ providedTurn = providedServerConfig.turn
+ providedStun = providedServerConfig.stun
+ if (ConnectivityHelper.isNetworkEnabled(context)) {
+ Timber.d("Provided Host Address: $providedHostAddress")
+ socket.connect(this, providedHostAddress, providedPort, pushMetaData) {
+ if (autoLogin) {
+ credentialLogin(credentialConfig)
+ }
+ }
+ } else {
+ socketResponseLiveData.postValue(SocketResponse.error("No Network Connection"))
+ }
+ }
+
+ fun connect(
+ providedServerConfig: TxServerConfiguration = TxServerConfiguration(),
+ tokenConfig: TokenConfig,
+ txPushMetaData: String? = null,
+ autoLogin: Boolean = true,
+ ) {
+ socketResponseLiveData =
+ MutableLiveData>(SocketResponse.initialised())
+ waitingForReg = true
+ invalidateGatewayResponseTimer()
+ resetGatewayCounters()
- Timber.d("Provided Host Address: $providedHostAddress")
+ providedHostAddress = if (txPushMetaData != null) {
+ val metadata = Gson().fromJson(txPushMetaData, PushMetaData::class.java)
+ processCallFromPush(metadata)
+ providedServerConfig.host
+ } else {
+ providedServerConfig.host
+ }
+
+ socket = TxSocket(
+ host_address = providedHostAddress!!,
+ port = providedServerConfig.port
+ )
providedPort = providedServerConfig.port
providedTurn = providedServerConfig.turn
providedStun = providedServerConfig.stun
if (ConnectivityHelper.isNetworkEnabled(context)) {
- socket.connect(this, providedHostAddress, providedPort, pushMetaData)
+ Timber.d("Provided Host Address: $providedHostAddress")
+ socket.connect(this, providedHostAddress, providedPort, pushMetaData) {
+ if (autoLogin) {
+ tokenLogin(tokenConfig)
+ }
+ }
} else {
socketResponseLiveData.postValue(SocketResponse.error("No Network Connection"))
}
}
+
/**
* Sets the callOngoing state to true. This can be used to see if the SDK thinks a call is ongoing.
*/
@@ -528,7 +640,9 @@ class TelnyxClient(
* @param config, the CredentialConfig used to log in
* @see [CredentialConfig]
*/
+ @Deprecated("telnyxclient.credentialLogin is deprecated. Use telnyxclient.connect(..) instead.")
fun credentialLogin(config: CredentialConfig) {
+
val uuid: String = UUID.randomUUID().toString()
val user = config.sipUser
val password = config.sipPassword
@@ -571,6 +685,7 @@ class TelnyxClient(
sessid = sessid
)
)
+ Timber.d("Auto login with credentialConfig")
socket.send(loginMessage)
}
@@ -648,6 +763,8 @@ class TelnyxClient(
* @param config, the TokenConfig used to log in
* @see [TokenConfig]
*/
+ @Deprecated("telnyxclient.tokenLogin is deprecated. Use telnyxclient.connect(...,autoLogin:true) " +
+ "with autoLogin set to true instead.")
fun tokenLogin(config: TokenConfig) {
val uuid: String = UUID.randomUUID().toString()
val token = config.sipToken
@@ -683,6 +800,39 @@ class TelnyxClient(
socket.send(loginMessage)
}
+ internal fun startStats(sessionId: UUID) {
+ debugReportStarted = true
+ val loginMessage = InitiateOrStopStatPrams(
+ type = "debug_report_start",
+ debugReportId = sessionId.toString(),
+ )
+ socket.send(loginMessage)
+ }
+
+ /**
+ * Sends Logged webrtc stats to backend
+ *
+ * @param config, the TokenConfig used to log in
+ * @see [TokenConfig]
+ */
+ internal fun sendStats(data: JsonObject, sessionId: UUID) {
+
+ val loginMessage = StatPrams(
+ debugReportId = sessionId.toString(),
+ reportData = data
+ )
+ socket.send(loginMessage)
+
+ }
+
+ internal fun stopStats(sessionId: UUID) {
+ debugReportStarted = false
+ val loginMessage = InitiateOrStopStatPrams(
+ debugReportId = sessionId.toString(),
+ )
+ socket.send(loginMessage)
+ }
+
/**
* Sets the global SDK log level
* Logging is implemented with Timber
@@ -764,7 +914,7 @@ class TelnyxClient(
} else {
SpeakerMode.EARPIECE
}
- }else{
+ } else {
SpeakerMode.EARPIECE
}
@@ -804,10 +954,12 @@ class TelnyxClient(
SpeakerMode.SPEAKER -> {
audioManager?.isSpeakerphoneOn = true
}
+
SpeakerMode.EARPIECE -> {
audioManager?.isSpeakerphoneOn = false
}
- SpeakerMode.UNASSIGNED -> audioManager?.isSpeakerphoneOn = false
+
+ SpeakerMode.UNASSIGNED -> audioManager?.isSpeakerphoneOn = false
}
}
@@ -843,7 +995,7 @@ class TelnyxClient(
* Stops any audio that the MediaPlayer is playing
* @see [MediaPlayer]
*/
- internal fun stopMediaPlayer() {
+ private fun stopMediaPlayer() {
if (mediaPlayer != null) {
mediaPlayer!!.stop()
mediaPlayer!!.reset()
@@ -1035,6 +1187,7 @@ class TelnyxClient(
override fun onConnectionEstablished() {
Timber.d("[%s] :: onConnectionEstablished", this@TelnyxClient.javaClass.simpleName)
socketResponseLiveData.postValue(SocketResponse.established())
+
}
override fun onErrorReceived(jsonObject: JsonObject) {
@@ -1044,6 +1197,7 @@ class TelnyxClient(
}
override fun onByeReceived(callId: UUID) {
+
Timber.d("[%s] :: onByeReceived", this.javaClass.simpleName)
val byeCall = calls[callId]
byeCall?.apply {
@@ -1150,20 +1304,46 @@ class TelnyxClient(
// generally occurs when a ringback setting is applied in inbound call settings
earlySDP = true
+ val callerIDName =
+ if (params.has("caller_id_name")) params.get("caller_id_name").asString else ""
+ val callerNumber =
+ if (params.has("caller_id_number")) params.get("caller_id_number").asString else ""
+
+ val mediaResponse = MediaResponse(
+ UUID.fromString(callId),
+ callerIDName,
+ callerNumber,
+ sessionId,
+ )
+ client.socketResponseLiveData.postValue(
+ SocketResponse.messageReceived(
+ ReceivedMessageBody(
+ SocketMethod.MEDIA.methodName,
+ mediaResponse
+ )
+ )
+ )
+
} else {
// There was no SDP in the response, there was an error.
callStateLiveData.postValue(CallState.DONE)
client.removeFromCalls(UUID.fromString(callId))
}
+
}
+
/*Stop local Media and play ringback from telnyx cloud*/
stopMediaPlayer()
}
override fun onOfferReceived(jsonObject: JsonObject) {
if (jsonObject.has("params")) {
- Timber.d("[%s] :: onOfferReceived [%s]", this@TelnyxClient.javaClass.simpleName, jsonObject)
+ Timber.d(
+ "[%s] :: onOfferReceived [%s]",
+ this@TelnyxClient.javaClass.simpleName,
+ jsonObject
+ )
val offerCall = call!!.copy(
context = context,
client = this,
@@ -1173,6 +1353,7 @@ class TelnyxClient(
providedTurn = providedTurn!!,
providedStun = providedStun!!
).apply {
+
val params = jsonObject.getAsJsonObject("params")
val offerCallId = UUID.fromString(params.get("callID").asString)
val remoteSdp = params.get("sdp").asString
@@ -1188,7 +1369,7 @@ class TelnyxClient(
val customHeaders =
params.get("dialogParams")?.asJsonObject?.get("custom_headers")?.asJsonArray
peerConnection = Peer(
- context, client, providedTurn, providedStun,
+ context, client, providedTurn, providedStun, offerCallId.toString(),
object : PeerConnectionObserver() {
override fun onIceCandidate(p0: IceCandidate?) {
super.onIceCandidate(p0)
@@ -1219,6 +1400,7 @@ class TelnyxClient(
this.inviteResponse = inviteResponse
}
+ offerCall.client.playRingtone()
addToCalls(offerCall)
offerCall.client.socketResponseLiveData.postValue(
SocketResponse.messageReceived(
@@ -1228,7 +1410,6 @@ class TelnyxClient(
)
)
)
- offerCall.client.playRingtone()
} else {
Timber.d(
"[%s] :: Invalid offer received, missing required parameters [%s]",
@@ -1316,7 +1497,7 @@ class TelnyxClient(
val callerNumber = params.get("caller_id_number").asString
peerConnection = Peer(
- context, client, providedTurn, providedStun,
+ context, client, providedTurn, providedStun, callId.toString(),
object : PeerConnectionObserver() {
override fun onIceCandidate(p0: IceCandidate?) {
super.onIceCandidate(p0)
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxConfig.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxConfig.kt
index 35640e60..c36094a6 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxConfig.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/TelnyxConfig.kt
@@ -41,7 +41,7 @@ data class CredentialConfig(
val ringtone: Any?,
val ringBackTone: Int?,
val logLevel: LogLevel = LogLevel.NONE,
- val autoReconnect: Boolean = true
+ val autoReconnect: Boolean = false
) : TelnyxConfig()
/**
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/model/PushMetaData.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/model/PushMetaData.kt
index ea65fe05..da82183c 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/model/PushMetaData.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/model/PushMetaData.kt
@@ -1,5 +1,6 @@
package com.telnyx.webrtc.sdk.model
+import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
data class PushMetaData(
@@ -15,6 +16,9 @@ data class PushMetaData(
val rtcIP: String? = null,
@SerializedName("rtc_port")
val rtcPort: Int? = null,
+ ) {
+ fun toJson() : String {
+ return Gson() .toJson(this)
+ }
-
- )
+}
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/peer/Peer.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/peer/Peer.kt
index 0a909c19..b4690bda 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/peer/Peer.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/peer/Peer.kt
@@ -5,16 +5,31 @@
package com.telnyx.webrtc.sdk.peer
import android.content.Context
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonArray
+import com.google.gson.JsonObject
import com.telnyx.webrtc.sdk.Config.DEFAULT_STUN
import com.telnyx.webrtc.sdk.Config.DEFAULT_TURN
import com.telnyx.webrtc.sdk.Config.PASSWORD
import com.telnyx.webrtc.sdk.Config.USERNAME
import com.telnyx.webrtc.sdk.TelnyxClient
import com.telnyx.webrtc.sdk.socket.TxSocket
-import org.webrtc.*
+import org.webrtc.AudioSource
+import org.webrtc.AudioTrack
+import org.webrtc.DefaultVideoDecoderFactory
+import org.webrtc.DefaultVideoEncoderFactory
+import org.webrtc.EglBase
+import org.webrtc.IceCandidate
+import org.webrtc.MediaConstraints
+import org.webrtc.PeerConnection
+import org.webrtc.PeerConnectionFactory
+import org.webrtc.SdpObserver
+import org.webrtc.SessionDescription
import timber.log.Timber
import java.util.*
+
/**
* Peer class that represents a peer connection which is required to initiate a call.
*
@@ -27,16 +42,22 @@ internal class Peer(
val client: TelnyxClient,
private val providedTurn: String = DEFAULT_TURN,
private val providedStun: String = DEFAULT_STUN,
+ private val callId: String = "",
observer: PeerConnection.Observer
) {
companion object {
private const val AUDIO_LOCAL_TRACK_ID = "audio_local_track"
private const val AUDIO_LOCAL_STREAM_ID = "audio_local_stream"
+ private const val CANDIDATE_LIMIT : Int = 5
+ private const val STATS_INTERVAL : Long = 2000L
+ private const val STATS_INITIAL : Long = 0L
}
private val rootEglBase: EglBase = EglBase.create()
+ internal var debugStatsId = UUID.randomUUID()
+
private val iceServer = getIceServers()
@@ -133,13 +154,75 @@ internal class Peer(
localAudioTrack.setVolume(1.0)
localStream.addTrack(localAudioTrack)
peerConnection?.addTrack(localAudioTrack)
- peerConnection?.getStats {
- it.statsMap.forEach { (key, value) ->
- Timber.tag("Stats").d("Key: $key, Value: $value")
- }
+ }
+
+ var gson: Gson = GsonBuilder().setPrettyPrinting().create()
+ private val timer = Timer()
+ var mainObject: JsonObject = JsonObject()
+ var audio: JsonObject = JsonObject()
+ var statsData: JsonObject = JsonObject()
+ var inBoundStats: JsonArray = JsonArray()
+ var outBoundStats: JsonArray = JsonArray()
+ var candidateParis: JsonArray = JsonArray()
+
+ internal fun stopTimer() {
+ client.stopStats(debugStatsId)
+ debugStatsId = null
+ mainObject = JsonObject()
+ timer.cancel()
+ }
+
+ internal fun startTimer() {
+ if (!client.debugReportStarted){
+ debugStatsId = UUID.randomUUID()
+ client.startStats(debugStatsId)
}
+ timer.schedule(object : TimerTask() {
+ override fun run() {
+ mainObject.addProperty("event", "stats")
+ mainObject.addProperty("tag", "stats")
+ mainObject.addProperty("peerId", "stats")
+ mainObject.addProperty("connectionId", callId)
+ peerConnection?.getStats {
+ it.statsMap.forEach { (key, value) ->
+ if (value.type == "inbound-rtp") {
+ val jsonInbound = gson.toJsonTree(value)
+ inBoundStats.add(jsonInbound)
+ }
+ if (value.type == "outbound-rtp") {
+ val jsonOutbound = gson.toJsonTree(value)
+ outBoundStats.add(jsonOutbound)
+ }
+ if (value.type == "candidate-pair" && candidateParis.size() < CANDIDATE_LIMIT) {
+ val jsonCandidatePair = gson.toJsonTree(value)
+ candidateParis.add(jsonCandidatePair)
+ }
+
+ }
+ }
+ audio.add("inbound", inBoundStats)
+ audio.add("outbound", outBoundStats)
+ audio.add("candidatePair", candidateParis)
+ statsData.add("audio", audio)
+ mainObject.add("data", statsData)
+ mainObject.addProperty("timestamp", System.currentTimeMillis())
+ if (inBoundStats.size() > 0 && outBoundStats.size() > 0 && candidateParis.size() > 0) {
+ inBoundStats = JsonArray()
+ outBoundStats = JsonArray()
+ candidateParis = JsonArray()
+ statsData = JsonObject()
+ audio = JsonObject()
+ Timber.tag("Stats Inbound").d("Inbound: ${mainObject.toString()}")
+ if (debugStatsId != null){
+ client.sendStats(mainObject, debugStatsId)
+ }
+ }
+
+ }
+ }, STATS_INITIAL, STATS_INTERVAL)
}
+
/**
* Initiates a call, creating an offer with a local SDP
* The offer creation is handled with an [SdpObserver]
@@ -149,6 +232,7 @@ internal class Peer(
private fun PeerConnection.call(sdpObserver: SdpObserver) {
val constraints = MediaConstraints().apply {
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
+ mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false"))
optional.add(MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"))
}
@@ -191,6 +275,7 @@ internal class Peer(
private fun PeerConnection.answer(sdpObserver: SdpObserver) {
val constraints = MediaConstraints().apply {
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
+ mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false"))
optional.add(MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"))
}
@@ -299,6 +384,7 @@ internal class Peer(
disconnect()
peerConnectionFactory.dispose()
}
+ stopTimer()
}
init {
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/socket/TxSocket.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/socket/TxSocket.kt
index 4c8ba61b..353a8c82 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/socket/TxSocket.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/socket/TxSocket.kt
@@ -128,9 +128,9 @@ class TxSocket(
"[%s] Connection established :: $host_address",
this@TxSocket.javaClass.simpleName
)
+ isConnected = true
onConnected(true)
listener.onConnectionEstablished()
- isConnected = true
}
override fun onMessage(webSocket: WebSocket, text: String) {
@@ -318,13 +318,6 @@ class TxSocket(
socket.cancel()
// socket.close(1000, "Websocket connection was asked to close")
}
- if (this::client.isInitialized) {
- launch(Dispatchers.IO) {
- client.dispatcher.executorService.shutdown()
- client.connectionPool.evictAll()
- client.cache?.close()
- }
- }
job.cancel("Socket was destroyed, cancelling attached job")
}
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/ReceivedResult.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/ReceivedResult.kt
index f6d4db79..bbfb85c6 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/ReceivedResult.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/ReceivedResult.kt
@@ -4,16 +4,9 @@
package com.telnyx.webrtc.sdk.verto.receive
-import android.os.Parcel
-import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import com.telnyx.webrtc.sdk.CustomHeaders
-import com.telnyx.webrtc.sdk.utilities.parseObject
-import com.telnyx.webrtc.sdk.utilities.toJsonString
-import kotlinx.parcelize.Parceler
-import kotlinx.parcelize.Parcelize
import java.util.*
-import kotlin.collections.ArrayList
/**
* Class representations of responses received on the socket connection
@@ -21,13 +14,13 @@ import kotlin.collections.ArrayList
sealed class ReceivedResult
-@Parcelize
+
data class DisablePushResponse(
@SerializedName("message")
val success: Boolean,
@SerializedName("message")
val message: String
-) : ReceivedResult(), Parcelable {
+) : ReceivedResult() {
companion object {
// Refactor for backend to send a boolean instead of a string
@@ -40,11 +33,10 @@ data class DisablePushResponse(
*
* @param sessid the session ID provided after logging in.
*/
-@Parcelize
data class LoginResponse(
@SerializedName("sessid")
val sessid: String
-) : ReceivedResult(), Parcelable
+) : ReceivedResult()
data class ByeResponse(
@@ -58,7 +50,6 @@ data class ByeResponse(
* @param callId a unique UUID that represents each ongoing call.
* @param sdp the Session Description Protocol that is received as a part of the answer to the call.
*/
-@Parcelize
data class AnswerResponse(
@SerializedName("callID")
val callId: UUID,
@@ -66,24 +57,7 @@ data class AnswerResponse(
val sdp: String,
@SerializedName("custom_headers")
val customHeaders: ArrayList = arrayListOf()
-) : ReceivedResult(), Parcelable {
- private companion object : Parceler {
- override fun AnswerResponse.write(parcel: Parcel, flags: Int) {
- parcel.writeString(sdp)
- parcel.writeString(callId.toString())
- parcel.writeString(customHeaders.toJsonString())
- }
-
- override fun create(parcel: Parcel): AnswerResponse {
- return AnswerResponse(
- callId = UUID.fromString(parcel.readString()),
- sdp = parcel.readString()!!,
- customHeaders = parcel.readString()?.parseObject>() ?: arrayListOf()
- )
- }
- }
-
-}
+) : ReceivedResult()
/**
* An invitation response containing the required information
@@ -94,7 +68,6 @@ data class AnswerResponse(
* @param callerIdNumber the number of the person who sent the invitation
* @param sessid the Telnyx Session ID on the socket connection.
*/
-@Parcelize
data class InviteResponse(
@SerializedName("callID")
val callId: UUID,
@@ -108,29 +81,7 @@ data class InviteResponse(
val sessid: String,
@SerializedName("custom_headers")
val customHeaders: ArrayList = arrayListOf()
-) : ReceivedResult(), Parcelable {
- private companion object : Parceler {
- override fun InviteResponse.write(parcel: Parcel, flags: Int) {
- parcel.writeString(sdp)
- parcel.writeString(callId.toString())
- parcel.writeString(callerIdNumber)
- parcel.writeString(callerIdName)
- parcel.writeString(sessid)
- parcel.writeString(customHeaders.toJsonString())
- }
-
- override fun create(parcel: Parcel): InviteResponse {
- return InviteResponse(
- callId = UUID.fromString(parcel.readString()),
- sdp = parcel.readString()!!,
- callerIdNumber = parcel.readString()!!,
- callerIdName = parcel.readString()!!,
- sessid = parcel.readString()!!,
- customHeaders = parcel.readString()?.parseObject>() ?: arrayListOf()
- )
- }
- }
-}
+) : ReceivedResult()
data class RingingResponse(
@SerializedName("callID")
@@ -144,3 +95,15 @@ data class RingingResponse(
@SerializedName("custom_headers")
val customHeaders: ArrayList = arrayListOf()
) : ReceivedResult()
+
+
+data class MediaResponse(
+ @SerializedName("callID")
+ val callId: UUID,
+ @SerializedName("callerIdName")
+ val callerIdName: String,
+ @SerializedName("callerIdNumber")
+ val callerIdNumber: String,
+ @SerializedName("sessid")
+ val sessid: String
+) : ReceivedResult()
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/SocketResponse.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/SocketResponse.kt
index a90d38b7..298b3969 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/SocketResponse.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/receive/SocketResponse.kt
@@ -6,7 +6,7 @@ package com.telnyx.webrtc.sdk.verto.receive
import com.telnyx.webrtc.sdk.model.SocketStatus
-data class SocketResponse(val status: SocketStatus, val data: T?, val errorMessage: String?) {
+data class SocketResponse(var status: SocketStatus, val data: T?, val errorMessage: String?) {
companion object {
fun established(): SocketResponse {
return SocketResponse(
@@ -16,6 +16,14 @@ data class SocketResponse(val status: SocketStatus, val data: T?, val err
)
}
+ fun initialised(): SocketResponse {
+ return SocketResponse(
+ SocketStatus.ESTABLISHED,
+ null,
+ null
+ )
+ }
+
fun messageReceived(data: T): SocketResponse {
return SocketResponse(
SocketStatus.MESSAGERECEIVED,
diff --git a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/send/ParamRequest.kt b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/send/ParamRequest.kt
index 8cc17d79..cf0982c9 100644
--- a/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/send/ParamRequest.kt
+++ b/telnyx_rtc/src/main/java/com/telnyx/webrtc/sdk/verto/send/ParamRequest.kt
@@ -7,6 +7,8 @@ package com.telnyx.webrtc.sdk.verto.send
import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName
import com.telnyx.webrtc.sdk.telnyx_rtc.BuildConfig
+import java.util.UUID
+
sealed class ParamRequest
data class LoginParam(
@@ -21,6 +23,30 @@ data class LoginParam(
val userAgent: String = "Android-" + BuildConfig.SDK_VERSION.toString(),
) : ParamRequest()
+data class StatPrams(
+ val type: String = "debug_report_data",
+ @SerializedName("debug_report_id")
+ val debugReportId: String = UUID.randomUUID().toString(),
+ @SerializedName("debug_report_data")
+ val reportData: JsonObject,
+ @SerializedName("debug_report_version")
+ val debugReportVersion: Int = 1,
+ @SerializedName("id")
+ val id: String = UUID.randomUUID().toString(),
+ val jsonrpc:String = "2.0"
+) : ParamRequest()
+
+data class InitiateOrStopStatPrams(
+ val type: String = "debug_report_stop",
+ @SerializedName("debug_report_id")
+ val debugReportId: String = UUID.randomUUID().toString(),
+ @SerializedName("debug_report_version")
+ val debugReportVersion: Int = 1,
+ @SerializedName("id")
+ val id: String = UUID.randomUUID().toString(),
+ val jsonrpc:String = "2.0"
+) : ParamRequest()
+
data class CallParams(
diff --git a/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/TelnyxClientTest.kt b/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/TelnyxClientTest.kt
index 5ea2771c..f00aba7e 100644
--- a/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/TelnyxClientTest.kt
+++ b/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/TelnyxClientTest.kt
@@ -141,6 +141,13 @@ class TelnyxClientTest : BaseTest() {
assertEquals(client.isNetworkCallbackRegistered, true)
}
+ @Test
+ fun `checkForMockCredentials`() {
+ assertEquals(MOCK_USERNAME_TEST, "")
+ assertEquals(MOCK_PASSWORD, "")
+ }
+
+
@Test
fun `disconnect connection`() {
client.socket = Mockito.spy(
@@ -176,16 +183,16 @@ class TelnyxClientTest : BaseTest() {
@Test
fun `login with valid credentials - login sent to socket and json received`() {
client = Mockito.spy(TelnyxClient(mockContext))
+ client.connect(txPushMetaData = null)
client.socket = Mockito.spy(
TxSocket(
host_address = "rtc.telnyx.com",
port = 14938,
)
)
- client.connect(txPushMetaData = null)
val config = CredentialConfig(
- MOCK_USERNAME,
+ MOCK_USERNAME_TEST,
MOCK_PASSWORD,
"Test",
"000000000",
@@ -203,13 +210,13 @@ class TelnyxClientTest : BaseTest() {
@Test
fun `login with invalid credentials - login sent to socket and json received`() {
client = Mockito.spy(TelnyxClient(mockContext))
+ client.connect(txPushMetaData = null)
client.socket = Mockito.spy(
TxSocket(
host_address = "rtc.telnyx.com",
port = 14938,
)
)
- client.connect(txPushMetaData = null)
val config = CredentialConfig(
"asdfasass",
@@ -233,6 +240,7 @@ class TelnyxClientTest : BaseTest() {
@Test
fun `login with valid token - login sent to socket and json received`() {
client = Mockito.spy(TelnyxClient(mockContext))
+ client.connect(txPushMetaData = null)
client.socket = Mockito.spy(
TxSocket(
host_address = "rtc.telnyx.com",
@@ -240,7 +248,6 @@ class TelnyxClientTest : BaseTest() {
)
)
- client.connect(txPushMetaData = null)
val config = TokenConfig(
MOCK_TOKEN,
@@ -261,6 +268,9 @@ class TelnyxClientTest : BaseTest() {
@Test
fun `login with invalid token - login sent to socket and json received`() {
client = Mockito.spy(TelnyxClient(mockContext))
+
+
+ client.connect(txPushMetaData = null)
client.socket = Mockito.spy(
TxSocket(
host_address = "rtc.telnyx.com",
@@ -268,8 +278,6 @@ class TelnyxClientTest : BaseTest() {
)
)
- client.connect(txPushMetaData = null)
-
val config = TokenConfig(
anyString(),
"test",
diff --git a/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/testhelpers/TestConstants.kt b/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/testhelpers/TestConstants.kt
index 03d5cc99..0603874c 100644
--- a/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/testhelpers/TestConstants.kt
+++ b/telnyx_rtc/src/test/java/com/telnyx/webrtc/sdk/testhelpers/TestConstants.kt
@@ -1,5 +1,5 @@
package com.telnyx.webrtc.sdk.testhelpers
-const val MOCK_USERNAME = ""
+const val MOCK_USERNAME_TEST = ""
const val MOCK_PASSWORD = ""
const val MOCK_TOKEN = ""