Viễn thông

Thư viện Android Telecom Jetpack mới giúp bạn dễ dàng cho nền tảng biết trạng thái cuộc gọi của mình. Bạn có thể tìm thấy mã nguồn và một ứng dụng mẫu trên GitHub.

Phần phụ thuộc và quyền

Trước tiên, hãy mở tệp build.gradle của mô-đun ứng dụng và thêm phần phụ thuộc cho mô-đun androidx Telecom:

dependencies {
    implementation ("androidx.core:core-telecom:1.0.0-alpha02")
}

Trong tệp kê khai ứng dụng, hãy khai báo rằng ứng dụng sử dụng quyền MANAGE_OWN_CALLS:

<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />

Đăng ký ứng dụng

Để cho Android biết về ứng dụng của mình, bạn phải đăng ký ứng dụng và các tính năng của ứng dụng đó. Thông tin này cho Android biết những tính năng mà ứng dụng của bạn hỗ trợ, chẳng hạn như gọi video, truyền trực tuyến cuộc gọi và giữ cuộc gọi. Thông tin này rất quan trọng để Android có thể tự định cấu hình để hoạt động với các tính năng của ứng dụng.

 private val callsManager = CallsManager(context)

var capabilities: @CallsManager.Companion.Capability Int =
    CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING

callsManager.registerAppWithTelecom(capabilities)

Tích hợp nền tảng

Có 2 tình huống gọi phổ biến nhất đối với mọi ứng dụng gọi là cuộc gọi đến và đi. Để đăng ký hướng lệnh gọi đúng cách và thông báo cho người dùng theo cách phù hợp, hãy sử dụng các API bên dưới.

Đăng ký cuộc gọi

Ví dụ sau minh hoạ cách đăng ký cuộc gọi đến:

companion object {
  const val APP_SCHEME = "MyCustomScheme"
  const val ALL_CALL_CAPABILITIES = (CallAttributes.SUPPORTS_SET_INACTIVE
    or CallAttributes.SUPPORTS_STREAM or CallAttributes.SUPPORTS_TRANSFER)

  const val INCOMING_NAME = "Luke"
  val INCOMING_URI: Uri = Uri.fromParts(APP_SCHEME, "", "")
  // Define all possible properties for CallAttributes
  val INCOMING_CALL_ATTRIBUTES =
    CallAttributes(
      INCOMING_NAME,
      INCOMING_URI,
      DIRECTION_INCOMING,
      CALL_TYPE_VIDEO_CALL,
      ALL_CALL_CAPABILITIES)
}

Đối tượng callAttributes có thể có các thuộc tính sau:

  • displayName: Tên của người gọi, cuộc họp hoặc phiên.
  • address: Địa chỉ của cuộc gọi. Lưu ý: địa chỉ này có thể mở rộng thành đường liên kết đến cuộc họp.
  • direction: Hướng của cuộc gọi, chẳng hạn như đến hoặc gọi đi.
  • callType: Thông tin liên quan đến dữ liệu đang được truyền, chẳng hạn như video và âm thanh.
  • callCapabilities: Đối tượng chỉ định khả năng thực hiện lệnh gọi.

Đối tượng callCapabilities có thể có các thuộc tính sau:

  • streaming: Cho biết cuộc gọi có hỗ trợ phát trực tuyến âm thanh đến một thiết bị chạy Android khác hay không.
  • transfer: Cho biết cuộc gọi có thể chuyển được hay không.
  • hold: Cho biết có thể giữ cuộc gọi hay không.

Thêm cuộc gọi

Phương thức addCall() sẽ trả về một ngoại lệ nếu thiết bị không hỗ trợ viễn thông hoặc nếu đã xảy ra lỗi khi thiết lập lệnh gọi.

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onIsCallAnswered, // Watch needs to know if it can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
        callControlScope = this
    }
}

Trả lời cuộc gọi

Sau khi thực hiện một cuộc gọi đến, bạn phải trả lời hoặc từ chối cuộc gọi đó. Bài kiểm tra này minh hoạ cách trả lời một lệnh gọi:

when (answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {

    }
}

Nếu đang thực hiện một lệnh gọi khác, answer() sẽ trả về CallControlResult.Error cho biết lý do không thể trả lời cuộc gọi. Trong trường hợp này, người dùng cần chuyển cuộc gọi còn lại sang trạng thái chờ.

Từ chối cuộc gọi

Để từ chối một cuộc gọi, hãy ngắt kết nối cuộc gọi với DisconnectCause.Rejected.

fun onRejectCall(){
    coroutineScope.launch {
        callControlScope?.let {
            it.disconnect(DisconnectCause(DisconnectCause.REJECTED))
        }
    }
}

Cuộc gọi đi

Khi thực hiện một cuộc gọi đi, sau khi bên từ xa trả lời, bạn phải đặt lệnh gọi thành đang hoạt động để cho nền tảng biết rằng cuộc gọi đang diễn ra:

when (setActive()) {
    is CallControlResult.Success -> {
        onIsCallActive()
    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

Giữ cuộc gọi

Nếu ứng dụng gọi điện hỗ trợ tính năng giữ cuộc gọi, hãy sử dụng setInActive để cho nền tảng biết rằng cuộc gọi của bạn hiện không hoạt động và các ứng dụng khác có thể tuỳ ý sử dụng micrô cũng như máy ảnh:

when (setInActive()) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

Ngắt kết nối

Để ngắt kết nối cuộc gọi, hãy thông báo cho bộ phận Viễn thông ngắt kết nối bằng cách cung cấp lý do hợp lệ:

coroutineScope.launch {
    callControlScope?.disconnect(DisconnectCause(DisconnectCause.LOCAL))
}

Định tuyến âm thanh

Trong khi gọi điện, đôi khi người dùng chuyển đổi giữa các thiết bị, chẳng hạn như loa, tai nghe hoặc thiết bị Bluetooth. Sử dụng các API availableEndpointscurrentCallEndpoint để xem danh sách tất cả thiết bị mà người dùng có thể sử dụng và thiết bị nào đang hoạt động.

Ví dụ này kết hợp cả hai quy trình để tạo một đối tượng giao diện người dùng nhằm hiển thị cho người dùng danh sách thiết bị và thiết bị nào đang hoạt động:

availableEndpoint = combine(callControlScope.availableEndpoints,
    callControlScope.currentCallEndpoint) {
    availableDevices: List<CallEndpoint>, activeDevice : CallEndpoint ->
    availableDevices.map {
        EndPointUI(
            isActive = activeDevice.endpointName == it.endpointName, it
        )
    }
}

Để thay đổi thiết bị đang hoạt động, hãy sử dụng requestEndpointChange với CallEndpoint mà bạn muốn thay đổi.

coroutineScope.launch {
     callControlScope?.requestEndpointChange(callEndpoint)
}

Hỗ trợ nền trước

Thư viện Viễn thông có tính năng hỗ trợ trên nền trước. Thư viện này sử dụng ConnectionService cho các thiết bị chạy Android 13 trở xuống. Đối với Android 14 trở lên, ứng dụng này sử dụng micrô và camera nền trước để hỗ trợ chính xác các dịch vụ trên nền trước. Tìm hiểu thêm về các dịch vụ trên nền trước.

Trong các yêu cầu về nền trước, ứng dụng phải đăng thông báo để người dùng biết rằng ứng dụng đang chạy ở nền trước.

Để đảm bảo ứng dụng được ưu tiên thực thi trên nền trước, hãy tạo một thông báo sau khi bạn đăng ký lệnh gọi với nền tảng. Mức độ ưu tiên khi ở nền trước sẽ bị xoá khi ứng dụng chấm dứt lệnh gọi hoặc thông báo của bạn không còn hợp lệ.

is TelecomCall.Registered -> {
    val notification = createNotification(call)
    notificationManager.notify(TELECOM_NOTIFICATION_ID, notification)
}

Hỗ trợ nền tảng

Đồng hồ có ứng dụng nhận điểm cuối chung. Ứng dụng này cung cấp cho người dùng giao diện cơ bản như trả lời, từ chối và ngắt kết nối các lệnh gọi. Ứng dụng hỗ trợ những thao tác này bằng cách triển khai các hàm lambda thông báo cho nền tảng rằng bạn đã thực hiện thao tác trên thiết bị.

Mỗi hàm lambda hết thời gian chờ sau 5 giây với một giao dịch không thành công nếu ứng dụng của bạn không phản hồi.

callsManager.addCall(
        attributes,
        onIsCallAnswered, // Watch/Auto need to know if they can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
//Call Scope
}
/**
  *  Can the call be successfully answered??
  *  TIP: Check the connection/call state to see if you can answer a call
  *  Example you may need to wait for another call to hold.
  **/
val onIsCallAnswered: suspend(type: Int) -> Unit = {}

/**
  * Can the call perform a disconnect
  */
val onIsCallDisconnected: suspend (cause: DisconnectCause) -> Unit = {}

/**
  *  Check is see if you can make the call active.
  *  Other calls and state might stop us from activating the call
  */
val onIsCallActive: suspend () -> Unit = {
    updateCurrentCall {
    }
}

/**
  * Check to see if you can make the call inactivate
  */
val onIsCallInactive: suspend () -> Unit = {}