Skip to main content

Android Pitel VoIP

android_pitel_voip​

Integrate VoIP call to your project

N|Solid

This document provides detailed instructions for integrating VoIP functionalities, including registering an account, making calls, and logging out using SdkPitel.

Demo​

Register extension call

1. Introduction​

Overview​

When user make call from Pitel Connect app, Pitel Server pushes a notification for all user login (who receives the call).

VoIP call flow

Features​

  • Register/Unregister Extension
  • Make and Receive Calls (outgoing call, incoming call)
  • Toggle Microphone
  • Toggle Speaker
  • Send DTMF
  • Hold/Un hold call
  • Blind transfer call

2. System Requirements​

  • Android SDK 21+
  • targetSdkVersion >= 33
  • Firebase configured for Android
  • google-services.json file placed in android/app/

3. Library Installation​

3.1 Configure proguard rules.​

The following rule needs to be added in the proguard-rules.pro to avoid obfuscated keys.

-keep class com.mobile.tech.** { *; }
-keep interface com.mobile.tech.** { *; }
-keepclassmembers class com.mobile.tech.** { public <init>(...); }

3.2. Configure Firebase​

  1. Go to Firebase Console.
  2. Create or select an existing Firebase project. Follow documentation for check PUSH_NOTIF.md. setup push notification (for Android).
  3. Add an Android application using your Package ID.
  4. Download google-services.json and place it in android/app/.
  5. Enable Cloud Messaging API in Google Cloud Console.

3.2. Add Dependencies​

Follow the docs to create a github personal access token.

Update settings.gradle.kts:

pluginManagement {
repositories {
.....
mavenCentral()
mavenLocal()
maven(url = "https://jitpack.io")
maven {
url = uri("https://maven.pkg.github.com/tel4vn-team/android-pitel-voip")
credentials {
username = USERNAME_GITHUB
password = TOKEN_GITHUB
}
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
.....
mavenCentral()
mavenLocal()
maven(url = "https://jitpack.io")
maven {
url = uri("https://maven.pkg.github.com/tel4vn-team/android-pitel-voip")
credentials {
username = USERNAME_GITHUB
password = TOKEN_GITHUB
}
}
}
}

Update android/app/build.gradle.kts:

dependencies {
implementation("com.pitel:pitelvoip:1.0.3") // Pitel VoIP library
implementation("com.google.firebase:firebase-messaging-ktx:23.3.1")
}

Update android/build.gradle:

classpath 'com.google.gms:google-services:4.3.10'

If use plugins block android/build.gradle:

plugins {
id("com.google.gms.google-services") version "4.3.10" apply false
}

Apply Firebase plugin in android/app/build.gradle:

apply plugin: 'com.google.gms.google-services'

4. Configure AndroidManifest.xml​

Add necessary permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

Add the FCM service:

<service android:name="com.google.firebase.messaging.FirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

5. Custom UI for call screen​

Change color in your file res/values/colors.xml (light/night)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="bg_call_screen">#4CAF50</color>
<color name="bg_group_action">#FFFFFF</color>
<color name="color_text_group_action">#212121</color>
<color name="color_icon_group_action">#212121</color>
<color name="color_group_action_disable">#CECECE</color>
<color name="color_icon_close">#212121</color>
<color name="color_text_display">#212121</color>
<color name="bg_dial_pad_dialog">#FFFFFF</color>
<color name="color_btn_digit">#FFFFFF</color>
<color name="color_text_digit">#212121</color>
<color name="color_text_dial_pad">#212121</color>
// Foreground service notification
<string name="sip_service_title">SIP Service is running</string>
<string name="sip_service_content">Please do not disable the service to ensure a stable connection</string>
// Back to call screen notification
<string name="back_to_call_screen_title">Ongoing call</string>
<string name="back_to_call_screen_content">Tap to return to the call screen</string>
</resources>

Properties

PropDescriptionType
bg_call_screenBackground color of the call screenHex color
bg_group_actionBackground color for group action buttons (e.g., mic, speaker, hold...)Hex color
color_text_group_actionText color on group action buttonsHex color
color_icon_group_actionIcon color on group action buttonsHex color
color_group_action_disableColor for disabled state of group action buttons (includes text & icon)Hex color
color_icon_closeIcon color of the close button in the dial padHex color
color_text_displayText color for phone number or call info displayHex color
bg_dial_pad_dialogBackground color of the dial padHex color
color_btn_digitBackground color of digit buttons in the dial padHex color
color_text_digitText color of digits on the digit buttonsHex color
color_text_dial_padGeneral text color in the dial pad (can be used for headers/subtitles)Hex color
sip_service_titleThe title displayed on the notification when the SIP service is running in the foreground.String
sip_service_contentThe content text shown in the notification indicating that a call is currently in progressString
back_to_call_screen_titleThe title used for the notification that allows the user to quickly return to the ongoing call screenString
back_to_call_screen_contentThe content text of the notification prompting the user to tap and return to the call interfaceString

6. Example​

Please clone repository and run Demo_SDK or checkout repo github to get example

7 Usage​

Registering an Account​

To register a user with SdkPitel, ensure that the username and password fields are not empty before proceeding.

SdkPitel.initConfig(
context = this,
callModel = CallModel(
accountFrom = "${EXTENSION_CALLER}",
accountFromName = "${EXTENSION_CALLER_NAME}",
passwordFrom = "${PASSWORD}"
),
callBackStatus = { status, callModel ->
when (status) {
CallEvent.ONGOING_CALL -> Log.d("MainActivity", "On going call...")
CallEvent.CONNECTING -> Log.d("MainActivity", "Registering...")
CallEvent.DISCONNECTED -> Log.d("MainActivity", "PitelStatus.DISCONNECTED")
CallEvent.CONNECTED -> Log.d("MainActivity", "Registered")
CallEvent.LOG_OUT -> Log.d("MainActivity", "Successfully unregistered!")
CallEvent.REGISTER_FAILED -> Log.d("MainActivity", "REGISTER FAILED. PLEASE RETRY")
else -> Log.d("MainActivity", "Status: $status - PitelStatus.OTHERS")
}
},
customCallScreen = CallActivity::class.java, // default CallActivity
)

Making outgoing call​

Before making a call, ensure that the recipient's phone number is provided.

SdkPitel.openCall(
context = this,
callModel = CallModel(
accountFrom = "${EXTENSION_CALLER}",
accountFromName = "${EXTENSION_CALLER_NAME}",
accountTo = "${EXTENSION_CALLEE}",
accountToName = "${EXTENSION_CALLEE_NAME}",
isIncoming = false,
),
)

Logging Out​

To unregister a user from SdkPitel, use the following method:

SdkPitel.logOutSdkPitel(this) {}

8. Example MainActivity.kt​

Update MainActivity.kt to initialize Firebase and SdkPitel with separate functions:

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.AppCompatEditText
import android.util.Log
import com.mobile.tech.sdk.SdkPitel
import com.mobile.tech.sdk.firebase.CallNotification
import com.mobile.tech.sdk.ui.call.CallActivity
import com.mobile.tech.sdk.model.CallModel

class MainActivity : AppCompatActivity() {
private lateinit var txtInputCall: AppCompatEditText
private lateinit var txtUser: AppCompatEditText
private lateinit var txtPass: AppCompatEditText

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

initView()
setupListeners()
}

private fun initView() {
txtInputCall = findViewById(R.id.txtInputCall)
txtUser = findViewById(R.id.txtUser)
txtPass = findViewById(R.id.txtPass)
}

private fun setupListeners() {
findViewById<AppCompatButton>(R.id.btnRegister).setOnClickListener { registerUser() }
findViewById<AppCompatButton>(R.id.btnCall).setOnClickListener { makeCall() }
findViewById<AppCompatButton>(R.id.btnLogout).setOnClickListener { logoutUser() }
}

private fun registerUser() {
if (txtUser.text.toString().isNotEmpty() && txtPass.text.toString().isNotEmpty()) {
SdkPitel.initConfig(
context = this,
callModel = CallModel(
accountFrom = txtUser.text.toString(),
accountFromName = txtUser.text.toString(),
passwordFrom = txtPass.text.toString()
),
callBackStatus = { status, callModel ->
if (isFinishing) {
return@initConfig
}
handleCallStatus(status, callModel)
},
customCallScreen = CallActivity::class.java,
)
} else {
Log.d("MainActivity", "Please enter both username and password!")
}
}

private fun makeCall() {
if (txtInputCall.text.toString().isNotEmpty()) {
SdkPitel.openCall(
context = this,
callModel = CallModel(
accountFrom = txtUser.text.toString(),
accountTo = txtInputCall.text.toString(),
accountToName = txtInputCall.text.toString(),
isIncoming = false,
),
)
} else {
Log.d("MainActivity", "Please enter the recipient's username!")
}
}

private fun logoutUser() {
SdkPitel.logOutSdkPitel(this) {
Log.d("MainActivity", "Not registered!")
}
}

private fun handleCallStatus(status: CallEvent, callModel: CallModel?) {
when (status) {
CallEvent.ONGOING_CALL -> Log.d("MainActivity", "On going call...")
CallEvent.CONNECTING -> Log.d("MainActivity", "Registering...")
CallEvent.DISCONNECTED -> Log.d("MainActivity", "PitelStatus.DISCONNECTED")
CallEvent.CONNECTED -> Log.d("MainActivity", "Registered")
CallEvent.LOG_OUT -> Log.d("MainActivity", "Successfully unregistered!")
CallEvent.REGISTER_FAILED -> Log.d("MainActivity", "REGISTER FAILED. PLEASE RETRY")
else -> Log.d("MainActivity", "Status: $status - PitelStatus.OTHERS")
}
}
}

9. Testing Push Notifications​

8.1. Using Firebase Console​

  1. Go to Firebase Console > Cloud Messaging.
  2. Click Send New Message.
  3. Select the Android app and enter the FCM token.
  4. Click Send Test Message.

8.2. Or Sending a Push Notification with cURL​

curl --location 'https://fcm.googleapis.com/v1/projects/{project_id}/messages:send' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {access_token}' \
--data '{
"message": {
"notification": {
"title": "",
"body": ""
},
"data": {
"uuid": "77712f3-9b56-4e26-96ea-382ea1206477",
"nameCaller": "Pitel Voip",
"phoneNumber": "101",
"appName": "Pitel Connnect",
"callType": "CALL"

},
"apns": {
"headers": {
"apns-priority": "1",
"sound": ""
},
"payload": {
"aps": {
"mutable-content": 1,
"content-available": 1
}
}
},
"android": {
"priority": "high"
},
"token": "{fcm_token}"
}
}'

10. Troubleshooting​

  • Ensure Cloud Messaging API is enabled in Google Cloud Console.
  • Verify google-services.json is correctly placed in android/app/.
  • On Android 13+, ensure POST_NOTIFICATIONS permission is granted.

This guide provides step-by-step instructions to integrate VoIP and push notifications into an Android application. If any issues arise, review the steps or contact technical support.