ไลบรารี Core-Telecom ช่วยลดความซับซ้อนของกระบวนการผสานรวมแอปพลิเคชันการโทรกับแพลตฟอร์ม Android โดยมีชุด API ที่มีประสิทธิภาพและสอดคล้องกัน
หากต้องการดูการใช้งานจริง คุณสามารถดูแอปพลิเคชันตัวอย่างได้ใน GitHub ดังนี้
- แอปตัวอย่างแบบเบา — ตัวอย่างที่เล็กที่สุด
ซึ่งแสดงการใช้งาน
Core-TelecomAPI เหมาะสำหรับการทำความเข้าใจแนวคิดพื้นฐานอย่างรวดเร็ว - แอปตัวอย่างที่ครอบคลุม (พัฒนาโดยทีม Core-Telecom Team) — แอปพลิเคชันที่มีฟีเจอร์หลากหลายมากขึ้นซึ่งแสดง ฟังก์ชันการทำงานขั้นสูงของ Telecom และแนวทางปฏิบัติแนะนำ ซึ่งเป็นแหล่งข้อมูลที่ยอดเยี่ยมสำหรับการทำความเข้าใจสถานการณ์การผสานรวมที่ซับซ้อน
ตั้งค่า Core-Telecom
เพิ่มทรัพยากร Dependency androidx.core:core-telecom ลงในไฟล์ build.gradle ของแอป
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
ประกาศสิทธิ์ MANAGE_OWN_CALLS ใน AndroidManifest.xml
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
ลงทะเบียนแอปของคุณ
ลงทะเบียนแอปการโทรกับ Android โดยใช้ CallsManager เพื่อเริ่มเพิ่มการโทรลงในระบบ เมื่อลงทะเบียน ให้ระบุความสามารถของแอป (เช่น การรองรับเสียง วิดีโอ)
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")
// Define capabilities supported by your call.
val callCapabilities = CallAttributesCompat.CallCapability(
supportsSetInactive = CallAttributesCompat.SUPPORTS_SET_INACTIVE // Call can be made inactive (implies hold)
)
return CallAttributesCompat(
displayName = callerName,
address = addressUri,
direction = CallAttributesCompat.DIRECTION_INCOMING,
callType = if (isVideoCall) CallAttributesCompat.CALL_TYPE_VIDEO_CALL else CallAttributesCompat.CALL_TYPE_AUDIO_CALL,
callCapabilitiesCompat = callCapabilities
)
}
เพิ่มการโทร
ใช้ callsManager.addCall กับ CallAttributesCompat และการเรียกกลับเพื่อเพิ่มการโทรใหม่ลงในระบบและจัดการการอัปเดตพื้นผิวระยะไกล 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))
จัดการปลายทางเสียงของการโทร
สังเกตและจัดการปลายทางเสียงโดยใช้ currentCallEndpoint, availableEndpoints และ isMuted Flow ภายใน CallControlScope
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)
}
การรองรับการทำงานอยู่เบื้องหน้า
ไลบรารีใช้ ConnectionService (Android 13 ระดับ API 33 ลงไป) หรือ
foregroundtypes (Android 14 ระดับ API 34 ขึ้นไป) เพื่อรองรับการทำงานอยู่เบื้องหน้า
แอปพลิเคชันต้องโพสต์การแจ้งเตือนเพื่อให้ผู้ใช้ทราบว่าแอปพลิเคชันกำลังทำงานอยู่เบื้องหน้า ซึ่งเป็นส่วนหนึ่งของข้อกำหนดการทำงานอยู่เบื้องหน้า
สร้างการแจ้งเตือนเมื่อเพิ่มการโทรด้วยแพลตฟอร์มเพื่อให้แอปได้รับการจัดลำดับความสำคัญในการดำเนินการเบื้องหน้า ระบบจะนำการจัดลำดับความสำคัญเบื้องหน้าออกเมื่อแอปสิ้นสุดการโทรหรือการแจ้งเตือนไม่ถูกต้องอีกต่อไป
ดูข้อมูลเพิ่มเติมเกี่ยวกับบริการที่ทำงานอยู่เบื้องหน้า
การรองรับพื้นผิวระยะไกล
อุปกรณ์ระยะไกล (สมาร์ทวอทช์ ชุดหูฟังบลูทูธ, Android Auto) สามารถจัดการการโทรได้โดยไม่ต้องโต้ตอบกับโทรศัพท์โดยตรง แอปของคุณต้องใช้แลมบ์ดาการเรียกกลับ (onAnswerCall, onSetCallDisconnected, onSetCallActive, onSetCallInactive) ที่ให้ไว้กับ CallsManager.addCall เพื่อจัดการการดำเนินการที่อุปกรณ์เหล่านี้เริ่มต้น
เมื่อมีการดำเนินการระยะไกลเกิดขึ้น ระบบจะเรียกใช้แลมบ์ดาที่เกี่ยวข้อง
การดำเนินการแลมบ์ดาเสร็จสมบูรณ์จะส่งสัญญาณว่าระบบประมวลผลคำสั่งแล้ว หากทำตามคำสั่งไม่ได้ แลมบ์ดาควรแสดงข้อยกเว้น
การติดตั้งใช้งานอย่างเหมาะสมจะช่วยให้ควบคุมการโทรได้อย่างราบรื่นในอุปกรณ์ต่างๆ ทดสอบอย่างละเอียดกับพื้นผิวระยะไกลต่างๆ
ส่วนขยายการโทร
นอกเหนือจากการจัดการสถานะการโทรและเส้นทางเสียงของการโทรแล้ว ไลบรารียังรองรับส่วนขยายการโทร ซึ่งเป็นฟีเจอร์ที่ไม่บังคับที่แอปของคุณสามารถติดตั้งใช้งานเพื่อมอบประสบการณ์การโทรที่สมบูรณ์ยิ่งขึ้นในพื้นผิวระยะไกล เช่น Android Auto ฟีเจอร์เหล่านี้ ได้แก่ ห้องประชุม การปิดเสียงการโทร และไอคอนการโทรเพิ่มเติม เมื่อแอปของคุณติดตั้งใช้งานส่วนขยาย ข้อมูลที่แอปให้ไว้จะซิงค์กับอุปกรณ์ที่เชื่อมต่อทั้งหมดซึ่งรองรับการแสดงส่วนขยายเหล่านี้ใน UI ด้วย ซึ่งหมายความว่าฟีเจอร์เหล่านี้จะพร้อมใช้งานในอุปกรณ์ระยะไกลเพื่อให้ผู้ใช้โต้ตอบได้ด้วย
สร้างการโทรด้วยส่วนขยาย
เมื่อสร้างการโทร แทนที่จะใช้ CallManager#addCall เพื่อสร้างการโทร
คุณสามารถใช้ CallManager#addCallWithExtensions แทน ซึ่งจะช่วยให้
แอปเข้าถึงขอบเขตอื่นที่เรียกว่า ExtensionInitializationScope ขอบเขตนี้ช่วยให้แอปพลิเคชันเริ่มต้นชุดส่วนขยายที่ไม่บังคับที่รองรับได้ นอกจากนี้ ขอบเขตนี้ยังมีเมธอดเพิ่มเติม 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 { newParticipan>ts -
participantExtension.updateParticipants(newParticipants)
}.launchIn(this)
// notify remote surfaces of which of the participants have their
// hands raised
raisedHandsFlow.onEach { newRaisedHan>ds -
raiseHandState.updateRaisedHands(newRaisedHands)
}.launchIn(this)
}
}
รองรับการปิดเสียงการโทร
การปิดเสียงการโทรช่วยให้ผู้ใช้ขอให้แอปปิดเสียงออกของการโทรโดยไม่ต้องปิดเสียงไมโครโฟนของอุปกรณ์จริง ระบบจะจัดการฟีเจอร์นี้ต่อการโทรแต่ละครั้ง ดังนั้น Jetpack จะจัดการความซับซ้อนของการจัดการสถานะการปิดเสียงทั่วโลกของการโทรผ่านมือถือที่กำลังดำเนินการอยู่ขณะที่การโทร VOIP ใช้งานอยู่ ซึ่งจะช่วยลดข้อผิดพลาดในการปิดเสียงออกในสถานการณ์ที่มีการโทรหลายครั้ง และยังช่วยให้มีฟีเจอร์ที่เป็นประโยชน์ เช่น ข้อความบ่งชี้ "คุณกำลังพูดอยู่" เมื่อผู้ใช้กำลังพูดโดยไม่ทราบว่ามีการเปิดใช้การปิดเสียงการโทร
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
) { newCallSilenceStateReque>st -
// 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)
}
}
รองรับไอคอนการโทร
ไอคอนการโทรช่วยให้แอประบุไอคอนที่กำหนดเองซึ่งแสดงการโทรที่จะแสดงบนพื้นผิวระยะไกลระหว่างการโทรได้ นอกจากนี้ คุณยังอัปเดตไอคอนนี้ได้ตลอดอายุการใช้งานของการโทร
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 { newIconU>ri -
callIconExtension.updateCallIconUri(newIconUri)
}.launchIn(this)
}
}
เพิ่มลงในบันทึกการโทรของระบบ
คุณสามารถเพิ่มการโทร VoIP ของแอปไปยังบันทึกการโทรของระบบเพื่อให้ปรากฏในแป้นโทรของระบบและผู้ใช้สามารถโทรกลับจากที่นั่นได้ ดูรายละเอียดได้ที่ ประวัติการโทรแบบรวม