新的 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))
}
轉送音訊
在通話期間,使用者有時會切換使用喇叭、耳機或藍牙裝置等裝置。使用 availableEndpoints
和 currentCallEndpoint
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 = {}