電信

新的 Android Telecom Jetpack 程式庫可讓您輕鬆告知平台呼叫的狀態。您可以在 GitHub 找到原始碼和範例應用程式。

依附元件和權限

首先,開啟應用程式模組 build.gradle 檔案,並新增 androidx Telecom 模組的依附元件:

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

在應用程式資訊清單中,宣告應用程式使用 MANAGE_OWN_CALLS 權限:

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

註冊應用程式

如要讓 Android 知道您的應用程式,您必須註冊應用程式及其功能。這可讓 Android 瞭解應用程式支援哪些功能,例如視訊通話、通話串流及保留通話。這項資訊十分重要,是為了讓 Android 自行設定與應用程式功能搭配使用。

 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)

平台整合

通話應用程式最常見的兩種呼叫情境是來電和撥出電話。如要正確註冊呼叫方向,並以適當的通知方式通知使用者,請使用下方的 API。

註冊通話

以下範例說明如何註冊來電:

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)
}

callAttributes 物件可包含下列屬性:

  • displayName:來電者、會議或工作階段的名稱。
  • address:呼叫的地址。請注意,這可以延伸至會議連結。
  • direction:通話方向,例如「來電」或「撥出」
  • callType:與所傳輸資料相關的資訊,例如影片和音訊。
  • callCapabilities:指定呼叫功能的物件。

callCapabilities 物件可包含下列屬性:

  • streaming:指出呼叫是否支援將音訊串流至其他 Android 裝置。
  • transfer:指出是否可轉接通話。
  • hold:表示可否保留通話。

新增通話

如果裝置不支援電信,或在設定呼叫時發生錯誤,addCall() 方法會傳回例外狀況。

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

接聽來電

開始來電後,您必須接聽或拒接來電。本範例說明如何接聽來電:

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

    }

    is CallControlResult.Error -> {

    }
}

如果其他呼叫正在進行中,answer() 會傳回 CallControlResult.Error,指出無法接聽來電的原因。在這種情況下,使用者必須保留其他呼叫。

拒接來電

如要拒接來電,請中斷通話與 DisconnectCause.Rejected 的通話。

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

撥出電話

撥打電話時,當遠端方接聽時,您必須將呼叫設為 active,讓平台知道通話正在進行中:

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

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

保留通話

如果呼叫應用程式支援保留通話,請使用 setInActive 告知平台您通話並未啟用,且其他應用程式可免費使用麥克風和相機:

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

    }

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

中斷連線

如要中斷通話,請提供有效原因,通知 Telecom 堆疊中斷連線:

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

轉送音訊

在通話期間,使用者有時會切換使用喇叭、耳機或藍牙裝置等裝置。使用 availableEndpointscurrentCallEndpoint API 取得使用者所有可用的裝置清單和使用中的裝置清單。

這個範例結合了兩個流程來建立 UI 物件,以便向使用者顯示裝置清單和其中之一:

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

如要變更使用中的裝置,請搭配要變更的 CallEndpoint 使用 requestEndpointChange

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

前景支援

Telecom 程式庫提供前景支援。此程式庫會針對搭載 Android 13 以下版本的裝置使用 ConnectionService。如果是 Android 14 以上版本,則會使用 foregroundtypes 麥克風和相機,以便正確支援前景服務。進一步瞭解前景服務

為因應前景要求,應用程式必須發布通知,讓使用者知道應用程式正在前景執行。

為了確保應用程式能取得前景執行的優先順序,請在向平台註冊呼叫後建立通知。當您的應用程式終止呼叫或通知失效時,系統會移除前景優先順序。

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

途徑支援

手錶有一般端點接收器應用程式。此應用程式可為使用者提供基本介面,例如接聽、拒絕及中斷通話。應用程式支援這些動作,方法是實作 lambda 函式,告知平台您已在裝置上執行動作。

如果應用程式沒有回應,則每個 lambda 函式會在交易失敗的 5 秒後逾時。

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 = {}