Skip to content

Commit

Permalink
Release 1.3.7 (#346)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
isaacakakpo1 authored Jul 25, 2024
1 parent 7c6c67a commit 658cecc
Show file tree
Hide file tree
Showing 21 changed files with 903 additions and 346 deletions.
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}

Expand Down Expand Up @@ -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
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

// If you need to use foreground services, you will need to add the following permissions
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL"/>

// Configure foregroundservice and set the foreground service type
// Remember to stopForegroundService when the call is answered or rejected
<service
android:name=".ForegroundService"
android:foregroundServiceType="phoneCall"
android:exported="true" />
```
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<UUID,Call> = telnyxClient.calls

// Retrieve a specific call by callId
val currentCall: Call? = calls[callId]
```



## ProGuard changes
NOTE:
Expand Down
17 changes: 16 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.telnyx.webrtc.sdk">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL"/>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Expand All @@ -13,6 +15,9 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>

<application
android:allowBackup="true"
Expand All @@ -23,7 +28,10 @@
android:name=".App"
android:theme="@style/Theme.TelnyxAndroidWebRTCSDK">

<activity android:name=".ui.MainActivity"/>
<activity android:name=".ui.MainActivity"
android:launchMode="singleInstance"

/>

<activity android:name=".ui.SplashScreenActivity"
android:theme="@style/SplashScreenTheme"
Expand Down Expand Up @@ -60,5 +68,12 @@
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="telnyx_channel" />

<service
android:enabled="true"
android:exported="true"
android:foregroundServiceType="phoneCall"
android:name=".NotificationsService"
/>

</application>
</manifest>
1 change: 1 addition & 0 deletions app/src/main/java/com/telnyx/webrtc/sdk/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
175 changes: 175 additions & 0 deletions app/src/main/java/com/telnyx/webrtc/sdk/NotificationsService.kt
Original file line number Diff line number Diff line change
@@ -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
)
}


}
2 changes: 1 addition & 1 deletion app/src/main/java/com/telnyx/webrtc/sdk/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ class CallInstanceFragment : Fragment(), NumberKeyboardListener {
when (data?.method) {
SocketMethod.INVITE.methodName -> {
//NOOP
}
SocketMethod.RINGING.methodName -> {

}
SocketMethod.MEDIA.methodName -> {

}
SocketMethod.BYE.methodName -> {

Expand Down
Loading

0 comments on commit 658cecc

Please sign in to comment.