eTelecom docs
Tiếng Việt
Tiếng Việt
  • HƯỚNG DẪN SỬ DỤNG
    • Trang quản trị cs.etelecom.vn
    • Trang quản trị dành cho Partner
    • Google Chrome Extension
    • App eTelecom
  • TÍCH HỢP API
    • Shop
      • Tài khoản
      • Sử dụng
    • Partner
      • Tài khoản
      • Sử dụng
    • Tổng đài
      • VoIP SDK
        • Android
        • iOS
        • Web
        • Flutter
      • API tổng đài
        • Partner lấy danh sách tổng đài
        • Partner tạo hotline
        • Partner lấy danh sách hotline
        • Partner kích hoạt tổng đài
        • Partner gán hotline vào tổng đài
        • Partner gỡ hotline ra khỏi tổng đài
        • Partner xóa hotline ra khỏi hệ thống
        • Tạo tổng đài
        • Lấy thông tin tổng đài
        • Lấy cấu hình tổng đài
        • Lấy danh sách hotline
        • Tạo nhân viên
        • Lấy danh sách nhân viên
        • Tạo máy nhánh
        • Lấy thông tin máy nhánh
        • Lấy danh sách máy nhánh
        • Gán máy nhánh cho nhân viên
        • Thay đổi hotline cho máy nhánh
        • Gỡ máy nhánh khỏi nhân viên
        • Lấy lịch sử cuộc gọi
    • Zalo Cloud
      • Hướng dẫn kết nối Zalo Cloud
      • Zalo Cloud API
        • Tạo liên kết uỷ quyền OA
        • Kết nối OA
        • Lấy refresh token
        • Lấy danh sách OA
        • Lấy thông tin OA
        • Cập nhật Webhook cho OA
        • Lấy danh sách người dùng
        • Gửi tin tư vấn dạng văn bản
        • Gửi tin Tư vấn theo mẫu yêu cầu thông tin người dùng
        • Gửi tin tư vấn dạng sticker
        • Gửi tin tư vấn dạng hình ảnh
        • Gửi tin tư vấn dạng file
        • Gửi tin giao dịch
        • Upload file
        • Gửi yêu cầu cấp quyền gọi
        • Kiểm tra khách hàng đã cấp quyền gọi
        • Lấy danh sách yêu cầu cấp quyền gọi
        • Tạo mẫu tin
        • Lấy thông tin chi tiết mẫu tin
        • Lấy danh sách mẫu tin
        • Khởi tạo Journey Token
        • Lấy thông tin chi tiết Journey Token
        • Lấy danh sách Journey Token
        • Lấy danh sách tin nhắn
        • Lấy thông tin chi tiết tin nhắn
        • Gửi ZNS
        • Gửi ZNS với Journey Token
  • SMS BRANDNAME
    • Hướng dẫn sử dụng
    • SMS API
      • Gửi SMS
      • Lấy trạng thái tin
      • Lấy thông tin chi tiết mẫu tin
Powered by GitBook
On this page
  • I. Thiết lập Android Studio project
  • II. Khởi tạo SDK
  • III. Đăng nhập máy nhánh (extension)
  • IV. Đăng xuất máy nhánh (extension)
  • V. Thực hiện cuộc gọi (outgoing call)
  • VI. Nhận cuộc gọi (incoming call)
  • 6.1. Thiết lập mobile push
  • 6.2. Hiện thực
  • VII. Cuộc gọi video
  1. TÍCH HỢP API
  2. Tổng đài
  3. VoIP SDK

Android

PreviousVoIP SDKNextiOS

Last updated 10 months ago

I. Thiết lập Android Studio project

Bước 1: Tải portsipSDK tại .

Bước 2: Giải nén file .zip vừa mới tải về, copy file AndroidSample/SIPSample_AndroidStudio/SIPSample/libs/portsipvoipsdk.jar vào thư mục app/libs của project Android

Bước 3: Compile Libs : Chuột phải vào file portsipvoipsdk.jar chọn Add as Library...

Bước 4: Khai báo các permission cần thiết trong file AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app">

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
  <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
  <!-- Các permission liên quan đến internet. -->
  
  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <!-- Cho phép ứng dụng có thể unlock điện thoại khi có cuộc gọi đến. -->
  <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
  <!-- Cho phép ứng dụng có thể mở giao diện fullscreen khi nhận thông báo cuộc gọi đến 
  lúc điện thoại đang ở trạng thái không active (khoá màn hình, kill app, không ở
  màn hình ứng dụng) -->
  <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
  <!-- Cho phép người dùng có thể thao tác khi máy đã ở trạng thái lock 
  (bấm chấp nhận/từ chối cuộc gọi). -->
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
  <!-- Cho phép sử dụng microphone. -->
  <uses-permission android:name="android.permission.CAMERA" />
  <!-- Cho phép sử dụng camera. -->
  <uses-permission android:name="android.permission.VIBRATE" />
  <!-- Cho phép ứng dụng điều khiển điện thoại rung khi có cuộc gọi đến. -->

</manifest>

Những ví dụ từ mục II trở đi áp dụng cho 2 file: MainActivity.kt và PortsipService.kt

II. Khởi tạo SDK

Ở file PortsipService.kt

  • Khai báo biến global val portsipSDK: PortSipSdk = PortSipSdk() . Dùng để gọi các API của PortsipSDK.

  • Class PortsipService phải implement interface: **OnPortSIPEvent .**

fun initialSDK() {
    portsipSDK.setOnPortSIPEvent(this)
    portsipSDK.CreateCallManager(application)
    
    portsipSDK.initialize(
      PortSipEnumDefine.ENUM_TRANSPORT_TCP, "0.0.0.0", 10002,
      PortSipEnumDefine.ENUM_LOG_LEVEL_NONE, "", 8, "PortSIP SDK for Android",
      0, 0, "", "", false, "")
    
    // những cấu hình liên quan đến Audio
    portsipSDK.addAudioCodec(PortSipEnumDefine.ENUM_AUDIOCODEC_OPUS)
    portsipSDK.addAudioCodec(PortSipEnumDefine.ENUM_AUDIOCODEC_G729)
    portsipSDK.addAudioCodec(PortSipEnumDefine.ENUM_AUDIOCODEC_PCMA)
    portsipSDK.addAudioCodec(PortSipEnumDefine.ENUM_AUDIOCODEC_PCMU)

    // những cấu hình liên quan đến Video
    portsipSDK.addVideoCodec(PortSipEnumDefine.ENUM_VIDEOCODEC_H264)
    portsipSDK.addVideoCodec(PortSipEnumDefine.ENUM_VIDEOCODEC_VP8)
    portsipSDK.addAudioCodec(PortSipEnumDefine.ENUM_VIDEOCODEC_VP9)
    portsipSDK.setVideoBitrate(-1, 512)
    portsipSDK.setVideoFrameRate(-1, 20)
    portsipSDK.setVideoResolution(480, 640)
    portsipSDK.setVideoNackStatus(true)
    
    // có thể đặt InstanceId là 1 constant bất kì.
    portsipSDK.setInstanceId("PORTSIP_INSTANCE_ID")
    
    portsipSDK.setLicenseKey("PORTSIP_UC_LICENSE")

}

Hàm này nên được gọi chung lúc với đăng nhập máy nhánh.

III. Đăng nhập máy nhánh (extension)

Đầu tiên, ở file MainActivity.kt, ta phải khởi chạy PortsipService trước

// cần lưu các data về extension (số máy nhánh, password, domain tenant, ...) vào storage của máy.
// sử dụng SharedPreferences
// Vì khi kill app, các data sẽ bị mất nên phải lưu xuống storage những thông tin này.

// Giải thích:
// số máy nhánh: (ví dụ: 1234, 5678, ...)
// password máy nhánh: (ví dụ: abcDefg123, xzyqerTT11, ...)
// domain tenant: (ví dụ: voip.example.com, ...)

val sharedPref = getSharedPreferences("CallManager", MODE_PRIVATE) ?: return 0
    with(sharedPref.edit()) {
      putString(getString(R.string.call_manager_ps_extension), số máy nhánh)
      putString(getString(R.string.call_manager_ps_password), password)
      putString(getString(R.string.call_manager_ps_domain), domain tenant)
      apply()
    }

Intent(this, PortsipService::class.java).also { intent ->
  startService(intent)
}

Ở file PortsipService.kt, sau khi startService như bước 1 thì onStartCommand của PortsipService được chạy, đăng nhập máy nhánh có thể được gọi lúc này, được hiện thực như ví dụ sau:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  registerPortsip()
  return super.onStartCommand(intent, flags, startId)
}

fun registerPortsip() {
  
  val sharedPref = getSharedPreferences("CallManager", MODE_PRIVATE)
  val psExtension = sharedPref.getString(getString(R.string.call_manager_ps_extension), "")
  val psPassword = sharedPref.getString(getString(R.string.call_manager_ps_password), "")
  val psDomain = sharedPref.getString(getString(R.string.call_manager_ps_domain), "")
  val pushMessage = sharedPref.getString(getString(R.string.call_manager_push_message), "") ?: ""

  // addSipMessageHeader dùng cho việc nhận cuộc gọi khi ứng dụng đang không active
  // ví dụ: tắt màn hình, kill app, ...
  // sẽ trình bày chi tiết ở mục VI.
  if (pushMessage.isNotEmpty()) {
    portsipSDK.clearAddedSipMessageHeaders()
    portsipSDK.addSipMessageHeader(-1, "REGISTER", 1, "x-p-push", pushMessage)
  }

  portsipSDK.removeUser()
  var code = portsipSDK.setUser(
    psExtension, psExtension, psExtension, psPassword, psDomain, "sip.etelecom.vn", 5063,
    "", 0, "", 0)
  
  if (code != PortSipErrorcode.ECoreErrorNone) {
    return
  }
  
  portsipSDK.unRegisterServer()
  code = portsipSDK.registerServer(90, 0)
  
}

Nên có 1 biến portsipRegistered dùng để lưu trạng thái đã register với portsip thành công hay chưa. Nếu đã register thành công thì chỉ cần gọi API refreshRegistration(), không cần qua 2 bước initialSDK() và registerPortsip() bên trên.

portsipSDK.refreshRegistration(0)

Nếu muốn register lại từ đầu thì phải Khởi tạo SDK lại như mục II.

Có 2 event sẽ xảy ra khi đăng nhập máy nhánh:

// Đăng nhập máy nhánh thành công
fun onRegisterSuccess(reason: String?, code: Int, sipMessage: String?) {}

// Đăng nhập máy nhánh thất bại
fun onRegisterFailure(reason: String?, code: Int, sipMessage: String?) {}

IV. Đăng xuất máy nhánh (extension)

Ở file PortsipService.kt

portsipSDK.removeUser()
portsipSDK.unRegisterServer()

V. Thực hiện cuộc gọi (outgoing call)

Ở file PortsipService.kt

  • Để thực hiện cuộc gọi:

_sessionID = portsipSDK.call(callee: String, sendSdp: Boolean, videoCall: Boolean)
// _sessionID là 1 biến global, khi một cuộc gọi bất kì được tạo ra thì sẽ có 1 session, biến _sessionID này sẽ lưu lại id của session đó.

// callee là số điện thoại user muốn gọi đến.
// sendSdp: có truyền lên Session Description Protocol hay không. Thường sẽ để bằng true
// videoCall: có thực hiện video call hay không.
  • Khi người nhận cuộc gọi chấp nhận cuộc gọi:

Sự kiện onInviteAnswered() xảy ra.

fun onInviteAnswered(
  sessionId: Long,
  callerDisplayName: String,
  caller: String,
  calleeDisplayName: String,
  callee: String,
  audioCodecNames: String,
  videoCodecNames: String,
  existsAudio: Boolean,
  existsVideo: Boolean,
  sipMessage: String
) {}

Khi người nhận cuộc gọi từ chối cuộc gọi/cuộc gọi không được bắt máy/hay vì 1 lý do nào đó mà cuộc gọi không thành công:

sự kiện onInviteFailure() xảy ra.

fun onInviteFailure(sessionId: Long, reason: String?, code: Int, sipMessage: String?) {}

Chủ động kết thúc cuộc gọi:

portsipSDK.hangUp(sessionID: Long)

VI. Nhận cuộc gọi (incoming call)

Cần áp dụng Mobile Push (push notification voip) để ứng dụng có thể nhận cuộc gọi trong các trường hợp không active:

  • Người dùng không ở màn hình ứng dụng

  • Khoá màn hình

  • Kill app

6.1. Thiết lập mobile push

Để sử dụng mobile push, cần khai báo ứng dụng Android với hệ thống portsip thông qua các bước:

  • Lấy 2 thông tin: 1 Server Key và 1 Sender ID trên Firebase của Project.

  • applicationId nằm trong file build.gradle (Module: android.app) ví dụ: vn.etelecom.app

  • Gửi cho eTelecom 3 thông tin này để tạo thêm Mobile Push.

6.2. Hiện thực

  • Khi có một cuộc gọi đến máy nhánh:

Sự kiện onInviteIncoming() xảy ra.

onInviteIncoming(
  sessionId: Long,
  callerDisplayName: String,
  caller: String,
  calleeDisplayName: String,
  callee: String,
  audioCodecs: String,
  videoCodecs: String,
  existsAudio: Boolean,
  existsVideo: Boolean,
  sipMessage: String
) {
// do something...
// Ví dụ:
// start 1 Activity hiển thị thông tin cuộc gọi đến
}
  • Tạo 1 service là FirebaseService, và khai báo nó ở AndroidManifest.xml

FirebaseService.kt
class FirebaseService : FirebaseMessagingService() {

  companion object {
    const val ACTION_REFRESH_PUSH_TOKEN = "REFRESH_PUSH_TOKEN"
    const val ACTION_PROCESS_PUSH_PBX = "PROCESS_PUSH_PBX"
    const val PUSH_TOKEN = "PUSH_TOKEN"
  }

  override fun onCreate() {
    super.onCreate()
  }

  // Khi eTelecom bắn 1 notification về việc có cuộc gọi đến máy nhánh của bạn.
  override fun onMessageReceived(remoteMessage: RemoteMessage) {
    // trong biến data này sẽ gồm thông tin về caller, callee, có video call hay không, ...
    val data: Map<String, String> = remoteMessage.data

    if ***app đang ở trạng thái unactive (kill app, screen off, ...)*** {

      Intent(this, PortsipService::class.java).also { intent ->
        intent.action = ACTION_PROCESS_PUSH_PBX
        startService(intent)
      }

    }
  }

  // Khi token của Firebase được reset
  override fun onNewToken(newToken: String) {
    Intent(this, PortsipService::class.java).also { intent ->
      intent.action = ACTION_REFRESH_PUSH_TOKEN
      intent.putExtra(PUSH_TOKEN, newToken)
      startService(intent)
    }
  }

}
AndroidManifest.xml
<service android:name=".FirebaseService" android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>
  • build.gradle(Project: android), thêm classpath sau đây:

buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.8'
    }
}
  • build.gradle(Module: android.app), thêm dependencies như sau:

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

dependencies {
  implementation platform('com.google.firebase:firebase-bom:28.1.0')
  implementation 'com.google.firebase:firebase-messaging'
}

Tạo notification channel: để dùng trong việc thông báo cuộc gọi đến. Ta nên gọi hàm này cùng lúc với đăng nhập máy nhánh.

fun initNotification() {
  notiManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

  val callChannel = NotificationChannel(BACKGROUND_INCOMING_CALL_CHANNEL_ID, "Cuộc gọi đến", NotificationManager.IMPORTANCE_HIGH)
  callChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
  
  notiManager?.createNotificationChannel(callChannel)  
}

Khi onStartCommand của PortsipService được chạy, đã trình bày chi tiết ở mục III.

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  if (intent != null) {
    // có thêm 2 tình huống cần xử lý bên dưới đây (bên cạnh tình huống đã được trình bày ở mục III.)
    // 1. Khi token của Firebase được refresh
    // 2. Khi kill app, hoặc tắt màn hình điện thoại, cuộc gọi đến sẽ được bắn qua Firebase.
  }
    
  return super.onStartCommand(intent, flags, startId)
}
  • Khi token của Firebase được refresh:

if (intent.action == FirebaseService.ACTION_REFRESH_PUSH_TOKEN) {
  firebasePushToken = intent.getStringExtra(FirebaseService.PUSH_TOKEN) ?: ""
  refreshPushToken(true)
  portsipSDK.refreshRegistration(0)
}
  • Khi kill app, hoặc tắt màn hình điện thoại, cuộc gọi đến sẽ được bắn qua Firebase và sẽ xử lý tại đây:

if (intent.action == FirebaseService.ACTION_PROCESS_PUSH_PBX) {

    // Đăng nhập máy nhánh lại, vì khi kill app thì trạng thái đăng nhập sẽ mất đi.
    registerPortsip()
    // Sau đó, hiển thị popup notification thông báo cuộc gọi đến.
    showPendingCallNotification()

}
  • Khi khởi động ứng dụng, lần đầu tiên start của PortsipService, ta phải lắng nghe token của Firebase rồi đăng nhập máy nhánh sau.

if (intent?.action == null || intent.action!!.isEmpty()) {
  FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
    if (!task.isSuccessful) {
      Log.w("FIREBASE", "Fetching FCM registration token failed", task.exception)
      return@OnCompleteListener
    }

    // Get new FCM registration token
    firebasePushToken = task.result ?: ""
    refreshPushToken(true)
    // Trường hợp Firebase init chậm, xảy ra sau khi đăng nhập máy nhánh thành công, ta sẽ cần refreshRegistration lại.
    portsipSDK.refreshRegistration(0)
  })

  registerPortsip()
}
  • Hàm refreshPushToken có thể hiện thực như sau:

fun refreshPushToken(willPush: Boolean) {
  if (firebasePushToken.isNotEmpty()) {
    portsipSDK.clearAddedSipMessageHeaders()
    val pushMessage = "device-os=android;device-uid=$firebasePushToken;allow-call-push=$willPush;allow-message-push=$willPush;app-id=$packageName"
    portsipSDK.addSipMessageHeader(-1, "REGISTER", 1, "x-p-push", pushMessage)

    // lưu pushMessage vào storage của máy
    val sharedPref = getSharedPreferences("CallManager", MODE_PRIVATE)
    with(sharedPref.edit()) {
      putString(getString(R.string.call_manager_push_message), pushMessage)
      apply()
    }
  }
}
  • Hàm showPendingNotification có thể hiện thực như sau:

fun showPendingCallNotification() {
    
  // Cần phải có 1 Activity để hiển thị view Cuộc gọi đến 
  // khi user bấm vào notification hoặc khi màn hình tắt.
  val fullScreenIntent = Intent(this, IncomingActivity::class.java)
  fullScreenIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

  val fullScreenPendingIntent = PendingIntent.getActivity(this, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
  
  val notification = Notification.Builder(this, BACKGROUND_INCOMING_CALL_CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_notification)
    .setColor(Color.argb(1, 47, 204, 112))
    .setContentTitle("Cuộc gọi đến từ")
    .setContentText(Html.fromHtml("<strong>${CallManager.callerParsed}</strong>", Html.FROM_HTML_MODE_LEGACY))
    .setOngoing(true)
    .setShowWhen(true)
    .setContentIntent(fullScreenPendingIntent)
    .setFullScreenIntent(fullScreenPendingIntent, true)
    .build()
  
  notiManager?.notify(BACKGROUND_INCOMING_CALL_NOTIFICATION, notification)
  
}

Một số thao tác user có thể thực hiện khi có cuộc gọi: Chấp nhận (bắt máy), Từ chối, Ngắt máy (sau khi đã chấp nhận), v.v....

//1. Chấp nhận cuộc gọi
portsipSDK.answerCall(sessionId: Long, videoCall: Boolean) {}

//2. Từ chối cuộc gọi
// code nên có giá trị là 486
portsipSDK.rejectCall(sessionId: Long, code: Int) {}

//3. Ngắt máy sau khi cuộc gọi đã được chấp nhận và đang diễn ra
portsipSDK.hangUp(sessionId: Long) {}

VII. Cuộc gọi video

Cần có 1 Activity thể hiện cuộc gọi Video, ví dụ như sau:

class VideoCallActivity: Activity(), View.OnClickListener {
  // video của user
  var localVideo
  // video của người đang đàm thoại với user
  var remoteVideo

  // khi cuộc gọi video được bắt đầu (1 trong caller hoặc callee chấp nhận cuộc gọi từ người còn lại)
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
  
    // phần giao diện của Activity này, được hiện thực trong file video_call_view.xml
    setContentView(R.layout.video_call_view)

    
    localVideo = findViewById(R.id.local_video_view)
    remoteVideo = findViewById(R.id.remote_video_view)

    portsipSDK.setRemoteVideoWindow(sessionId: Long, remoteVideo)
    portsipSDK.displayLocalVideo(true, true, localVideo)
    
    // nếu user tắt cam thì tham số send bằng false, ngược lại thì bằng true
    portsipSDK.sendVideo(sessionId: Long, send: Boolean)
  }

  // khi cuộc gọi kết thúc, Activity bị dismiss thì cần release các video này ra.
  override fun onDestroy() {
    super.onDestroy()

    portsipSDK.displayLocalVideo(false, false, null)
    localVideo.release()

    portsipSDK.setRemoteVideoWindow(sessionId: Long, null)
    remoteVideo.release()
  }
}

Nên dùng biến portsipSDK ở file PortsipService.kt để không xảy ra hiện tượng nhiều instance của 1 biến được tạo ra, sẽ phát sinh nhiều bug không mong muốn.

Giao diện của Activity bên trên, có thể hiện thực ở file video_call_view.xml như ví dụ sau:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:background="#EEEEEE"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

  <!-- Remote Video: video của người đang đàm thoại với user -->
  <LinearLayout
            android:id="@+id/remote_video_view_wrapper"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:background="#333"
            android:orientation="vertical">

        <com.portsip.PortSIPVideoRenderer
                android:id="@+id/remote_video_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
  </LinearLayout>

  <!-- Local Video: video của user -->
  <LinearLayout
            android:id="@+id/local_video_view_wrapper"
            android:layout_width="120dp"
            android:layout_height="166dp"
            android:layout_alignParentTop="true"
            android:layout_marginTop="70dp"
            android:layout_alignParentEnd="true"
            android:layout_marginEnd="22dp"
            android:clipChildren="true"
            android:background="@drawable/transparent_rounded_corner">

        <com.portsip.PortSIPVideoRenderer
                android:id="@+id/local_video_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
  </LinearLayout>

</FrameLayout>

Gọi ra và nhận cuộc gọi đến:

// 1. Gọi ra, với videoCall bằng true
portsipSDK.call(callee: String, true, videoCall: Boolean)

// 2. Nhận cuộc gọi đến
onInviteIncoming(
  sessionId: Long,
  callerDisplayName: String,
  caller: String,
  calleeDisplayName: String,
  callee: String,
  audioCodecs: String,
  videoCodecs: String,
  existsAudio: Boolean,
  existsVideo: Boolean,
  sipMessage: String
) {
// existsVideo để cho biết cuộc gọi hiện tại có phải là videoCall hay không.
}

Tài liệu tham khảo:

https://www.portsip.com/docs/sdk/manual/portsip-voip-sdk-user-manual-android.pdf
đây