Core-Telecom 程式庫提供一組穩健且一致的 API,可簡化將通話應用程式與 Android 平台整合的程序
如要瞭解實際的實作方式,請參閱 GitHub 上的範例應用程式:
- 輕量型範例應用程式:這個最簡單的範例會示範
Core-TelecomAPI 的用法。適合快速瞭解基本概念。 - 完整範例應用程式 (由 Core-Telecom 團隊開發):這個應用程式功能更豐富,可展示進階電信功能和最佳做法。這項資源非常適合用來瞭解複雜的整合情境。
設定 Core-Telecom
在應用程式的 build.gradle 檔案中新增 androidx.core:core-telecom 依附元件:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
在 AndroidManifest.xml 中宣告 MANAGE_OWN_CALLS 權限:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
註冊應用程式
使用 CallsManager 向 Android 註冊通話應用程式,開始將通話新增至系統。註冊時,請指定應用程式的功能 (例如音訊、影片支援):
val callsManager = CallsManager(context)
val capabilities: @CallsManager.Companion.Capability Int =
(CallsManager.CAPABILITY_BASELINE or
CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)
callsManager.registerAppWithTelecom(capabilities)
通話管理
使用 Core-Telecom API 建立及管理通話生命週期。
建立通話
CallAttributesCompat 物件會定義專屬通話的屬性,可具有下列特徵:
displayName:來電者姓名。address:通話地址 (例如電話號碼、會議連結)。direction:來電或外撥。callType:音訊或視訊。callCapabilities:支援轉接和保留。
以下範例說明如何建立來電:
fun createIncomingCallAttributes(
callerName: String,
callerNumber: String,
isVideoCall: Boolean): CallAttributesCompat {
val addressUri = Uri.parse("YourAppScheme:$callerNumber")
return CallAttributesCompat(
displayName = callerName,
address = addressUri,
direction = CallAttributesCompat.DIRECTION_INCOMING,
callType = if (isVideoCall) {
CallAttributesCompat.CALL_TYPE_VIDEO_CALL
} else {
CallAttributesCompat.CALL_TYPE_AUDIO_CALL
},
callCapabilities = CallAttributesCompat.SUPPORTS_SET_INACTIVE
)
}
新增通話
搭配 CallAttributesCompat 和回呼使用 callsManager.addCall,即可在系統中新增呼叫,並管理遠端介面更新。callControlScope 區塊中的 addCall 主要可讓應用程式轉換通話狀態,並接收音訊更新:
try {
callsManager.addCall(
INCOMING_CALL_ATTRIBUTES,
onAnswerCall, // Watch needs to know if it can answer the call.
onSetCallDisconnected,
onSetCallActive,
onSetCallInactive
) {
// The call was successfully added once this scope runs.
callControlScope = this
}
}
catch(addCallException: Exception){
// Handle the addCall failure.
}
接聽來電
在 CallControlScope 內接聽來電:
when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
is CallControlResult.Success -> { /* Call answered */ }
is CallControlResult.Error -> { /* Handle error */ }
}
拒接來電
使用 disconnect() 和 DisconnectCause.REJECTED 在 CallControlScope 中拒接來電:
disconnect(DisconnectCause(DisconnectCause.REJECTED))
啟用撥出電話
在遠端接聽電話後,將外撥電話設為有效:
when (val result = setActive()) {
is CallControlResult.Success -> { /* Call active */ }
is CallControlResult.Error -> { /* Handle error */ }
}
保留通話
使用 setInactive() 保留通話:
when (val result = setInactive()) {
is CallControlResult.Success -> { /* Call on hold */ }
is CallControlResult.Error -> { /* Handle error */ }
}
中斷通話
如要使用 disconnect() 透過 DisconnectCause 掛斷通話,請按照下列步驟操作:
disconnect(DisconnectCause(DisconnectCause.LOCAL))
管理通話音訊端點
使用 CallControlScope 中的 currentCallEndpoint、availableEndpoints 和 isMuted Flow,觀察及管理音訊端點。使用 Telecom 時,請勿使用 AudioManager#setCommunicationDevice 或 AudioManager#startBluetoothSco API 管理音訊路徑,否則通話會發生音訊問題。
fun observeAudioStateChanges(callControlScope: CallControlScope) {
with(callControlScope) {
launch { currentCallEndpoint.collect { /* Update UI */ } }
launch { availableEndpoints.collect { /* Update UI */ } }
launch { isMuted.collect { /* Handle mute state */ } }
}
}
使用 requestEndpointChange() 變更使用的音訊裝置:
coroutineScope.launch {
callControlScope.requestEndpointChange(callEndpoint)
}
前景支援
這個程式庫會在 Android 13 (API 級別 33) 以下版本使用 ConnectionService,或在 Android 14 (API 級別 34) 以上版本使用前景服務類型,以提供前景支援。
如要在應用程式處於背景時保持通話連線,請在前景 Service (例如 LifecycleService) 內代管 CallsManager,並在 AndroidManifest.xml 中宣告 phoneCall 前景服務類型:
<service
android:name=".TelecomVoipService"
android:foregroundServiceType="phoneCall" />
根據前景規定,應用程式必須發布 NotificationCompat.CallStyle 通知,讓使用者知道通話正在前景中進行。如要確保應用程式取得前景執行優先權,請在新增平台呼叫後,使用 startForeground 將服務升級為前景服務:
startForeground(
notificationId,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
)
遠端 Surface 支援
遠端裝置 (智慧手錶、藍牙耳機、Android Auto) 能夠管理通話,不必直接與手機互動。應用程式必須實作提供給 CallsManager.addCall 的回呼 lambda (onAnswerCall、onSetCallDisconnected、onSetCallActive、onSetCallInactive),以處理這些裝置啟動的動作。
發生遠端動作時,系統會叫用對應的 lambda。
Lambda 成功完成,表示指令已處理完畢。如果無法遵守指令,lambda 應擲回例外狀況。
正確實作可確保不同裝置都能順暢控制通話。 使用各種遠端介面徹底測試。
來電額外資訊
除了管理通話狀態和音訊路徑外,這個程式庫也支援通話擴充功能。應用程式可實作這些選用功能,在遠端介面 (例如 Android Auto) 上提供更豐富的通話體驗。這些功能包括會議室、通話靜音和額外的通話圖示。應用程式實作擴充功能時,應用程式提供的資訊會與所有已連結的裝置同步,這些裝置也支援在 UI 中顯示這些擴充功能。也就是說,使用者也能在遠端裝置上使用這些功能。
使用擴充功能建立通話
建立通話時,您可以使用 CallsManager.addCallWithExtensions,讓應用程式存取名為 ExtensionInitializationScope 的不同範圍,而不是使用 CallsManager.addCall 建立通話。這個範圍可讓應用程式初始化支援的一組選用擴充功能。此外,這個範圍還提供額外方法 onCall,可在擴充功能交換和初始化完成後,將 CallControlScope 提供給應用程式。
scope.launch {
mCallsManager.addCallWithExtensions(
attributes,
onAnswer,
onDisconnect,
onSetActive,
onSetInactive
) {
// Initialize extension-specific code...
// After the call has been initialized, perform in-call actions
onCall {
// Example: process call state updates
callStateFlow.onEach { newState ->
// handle call state updates and notify telecom
}.launchIn(this)
// Use initialized extensions...
}
}
}
支援通話參與者
如果應用程式支援會議或群組通話的通話參與者,請使用 addParticipantExtension 宣告支援這項擴充功能,並在參與者變更時使用相關 API 更新遠端介面。
mCallsManager.addCallWithExtensions(...) {
// Initialize extensions...
// Notifies Jetpack that this app supports the participant
// extension and provides the initial participants state in the call.
val participantExtension = addParticipantExtension(
initialParticipants,
initialActiveParticipant
)
// After the call has been initialized, perform in-call control actions
onCall {
// other in-call control and extension actions...
// Example: update remote surfaces when the call participants change
participantsFlow.onEach { newParticipants ->
participantExtension.updateParticipants(newParticipants)
}.launchIn(this)
}
}
除了通知遠端介面通話參與者,您也可以使用 ParticipantExtension#updateActiveParticipant 更新活躍參與者。
此外,系統也支援與通話參與者相關的選用動作。應用程式可以使用 ParticipantExtension#addRaiseHandSupport 支援參與者在通話中舉手的概念,並查看其他哪些參與者也舉手。
mCallsManager.addCallWithExtensions(...) {
// Initialize extensions...
// Notifies Jetpack that this app supports the participant
// extension and provides the initial list of participants in the call.
val participantExtension = addParticipantExtension(initialParticipants)
// Notifies Jetpack that this app supports the notion of participants
// being able to raise and lower their hands.
val raiseHandState = participantExtension.addRaiseHandSupport(
initialRaisedHands
) { onHandRaisedStateChanged ->
// handle this user's raised hand state changed updates from
// remote surfaces.
}
// After the call has been initialized, perform in-call control actions
onCall {
// other in-call control and extension actions...
// Example: update remote surfaces when the call participants change
participantsFlow.onEach { newParticipants ->
participantExtension.updateParticipants(newParticipants)
}.launchIn(this)
// notify remote surfaces of which of the participants have their
// hands raised
raisedHandsFlow.onEach { newRaisedHands ->
raiseHandState.updateRaisedHands(newRaisedHands)
}.launchIn(this)
}
}
支援通話靜音
通話靜音功能可讓使用者要求應用程式將通話的輸出音訊設為靜音,而不必實際將裝置的麥克風設為靜音。這項功能是依通話管理,因此在 VoIP 通話進行中,Jetpack 會處理管理進行中行動網路通話全域靜音狀態的複雜性。這項做法可減少在多方通話情境中,將外送音訊設為靜音時發生錯誤的機率,同時啟用實用功能,例如在使用者說話時,如果他們未察覺已啟用通話靜音功能,系統會顯示「你正在說話」的提示。
mCallsManager.addCallWithExtensions(...) {
// Initialize extensions...
// Add support for locally silencing the call's outgoing audio and
// register a handler for when the user changes the call silence state
// from a remote surface.
val callSilenceExtension = addLocalCallSilenceExtension(
initialCallSilenceState = false
) { newCallSilenceStateRequest ->
// handle the user's request to enable/disable call silence from
// a remote surface
}
// After the call has been initialized, perform in-call control actions
onCall {
// other in-call control and extension actions...
// When the call's call silence state changes, update remote
// surfaces of the new state.
callSilenceState.onEach { isSilenced ->
callSilenceExtension.updateIsLocallySilenced(isSilenced)
}.launchIn(this)
}
}
支援電話圖示
應用程式可透過通話圖示指定代表通話的自訂圖示,在通話期間顯示於遠端 Surface。通話期間,這個圖示也可能會更新。
mCallsManager.addCallWithExtensions(...) {
// Initialize extensions...
// Add support for a custom call icon to be displayed during the
// lifetime of the call.
val callIconExtension = addCallIconExtension(
initialCallIconUri = initialUri
)
// After the call has been initialized, perform in-call control actions
onCall {
// other in-call control and extension actions...
// When the call's icon changes, update remote surfaces by providing
// the new URI.
callIconUri.onEach { newIconUri ->
callIconExtension.updateCallIconUri(newIconUri)
}.launchIn(this)
}
}
新增至系統通話記錄
您可以將應用程式的 VoIP 通話新增至系統通話記錄,讓通話記錄顯示在系統撥號程式中,方便使用者從撥號程式回撥。詳情請參閱「整合通話記錄」。