ไลบรารี Core-Telecom จะช่วยปรับปรุงกระบวนการผสานรวมแอปพลิเคชันการโทรเข้ากับแพลตฟอร์ม Android ด้วยการจัดเตรียมชุด API ที่มีประสิทธิภาพและสม่ำเสมอ
หากต้องการดูการใช้งานจริง คุณสามารถดูตัวอย่างแอปพลิเคชันบน GitHub ได้ที่
- แอปตัวอย่างขนาดเบา — ตัวอย่างการใช้งาน Core-TelecomAPI ขั้นต่ำ เหมาะสำหรับการทำความเข้าใจแนวคิดพื้นฐานอย่างรวดเร็ว
- ตัวอย่างแอปที่ครอบคลุม (พัฒนาโดยทีม Core-Telecom) — แอปพลิเคชันที่มาพร้อมฟีเจอร์มากมายซึ่งแสดงฟังก์ชันการทำงานขั้นสูงและแนวทางปฏิบัติแนะนำของโทรคมนาคม นี่เป็นแหล่งข้อมูลที่ยอดเยี่ยมในการทำความเข้าใจสถานการณ์การผสานรวมที่ซับซ้อน
ตั้งค่า Core-Telecom
เพิ่มการพึ่งพา 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))
จัดการปลายทางเสียงของสายเรียกเข้า
ตรวจสอบและจัดการอุปกรณ์ปลายทางเสียงโดยใช้ Flow currentCallEndpoint,
availableEndpoints และ isMuted ภายใน 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 ขึ้นไป) เพื่อรองรับการทำงานอยู่เบื้องหน้า
แอปพลิเคชันต้องโพสต์การแจ้งเตือนเพื่อให้ผู้ใช้ทราบว่าแอปพลิเคชันทำงานอยู่เบื้องหน้าตามข้อกำหนดของเบื้องหน้า
สร้างการแจ้งเตือนเมื่อคุณเพิ่มการเรียกใช้กับแพลตฟอร์มเพื่อให้แอปของคุณได้รับสิทธิ์ดำเนินการในเบื้องหน้า ระบบจะนำลำดับความสำคัญในเบื้องหน้าออกเมื่อแอปสิ้นสุดการโทรหรือการแจ้งเตือนของคุณไม่ถูกต้องอีกต่อไป
ดูข้อมูลเพิ่มเติมเกี่ยวกับบริการที่ทำงานอยู่เบื้องหน้า
การสนับสนุน Surface จากระยะไกล
อุปกรณ์ระยะไกล (สมาร์ทวอทช์ ชุดหูฟังบลูทูธ, Android Auto) สามารถจัดการการโทรได้โดยไม่ต้องโต้ตอบกับโทรศัพท์โดยตรง แอปของคุณต้องใช้แลมดาการเรียกกลับ (onAnswerCall, onSetCallDisconnected, onSetCallActive,
onSetCallInactive) ที่ระบุให้กับ CallsManager.addCall เพื่อจัดการการดำเนินการที่อุปกรณ์เหล่านี้เริ่ม
เมื่อเกิดการดำเนินการระยะไกล ระบบจะเรียกใช้ Lambda ที่เกี่ยวข้อง
สัญญาณ Lambda เสร็จสมบูรณ์ที่ประมวลผลคําสั่ง หากไม่สามารถปฏิบัติตามคําสั่งได้ Lambda ควรแสดงข้อยกเว้น
การติดตั้งใช้งานอย่างเหมาะสมช่วยให้ควบคุมการโทรในอุปกรณ์ต่างๆ ได้อย่างราบรื่น ทดสอบอย่างละเอียดกับแพลตฟอร์มระยะไกลต่างๆ
ชิ้นงานการโทร
นอกจากการจัดการสถานะการโทรและเส้นทางเสียงของการโทรแล้ว ไลบรารียังรองรับส่วนขยายการโทร ซึ่งเป็นฟีเจอร์ที่ไม่บังคับที่แอปของคุณนำมาใช้เพื่อให้ได้รับประสบการณ์การโทรที่สมบูรณ์ยิ่งขึ้นบนแพลตฟอร์มระยะไกล เช่น Android Auto ฟีเจอร์เหล่านี้ ได้แก่ ห้องประชุม การปิดเสียงการโทร และไอคอนการโทรเพิ่มเติม เมื่อแอปของคุณใช้ส่วนขยาย ข้อมูลที่แอปให้ไว้จะซิงค์กับอุปกรณ์ที่เชื่อมต่อทั้งหมดที่รองรับการแสดงส่วนขยายเหล่านี้ใน UI ด้วย ซึ่งหมายความว่าฟีเจอร์เหล่านี้จะพร้อมใช้งานบนอุปกรณ์ระยะไกลเพื่อให้ผู้ใช้โต้ตอบด้วย
สร้างการโทรด้วยส่วนขยาย
เมื่อสร้างการโทร คุณสามารถใช้ CallManager#addCallWithExtensions แทนการใช้ CallManager#addCall เพื่อสร้างการโทร ซึ่งจะให้สิทธิ์เข้าถึงขอบเขตอื่นที่เรียกว่า 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 { 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)
        }
    }
ปิดเสียงสายเรียกเข้าจากทีมสนับสนุน
การปิดเสียงการโทรช่วยให้ผู้ใช้สามารถขอให้แอปปิดเสียงขาออกของการโทรได้โดยไม่ต้องปิดเสียงไมโครโฟนของอุปกรณ์ ฟีเจอร์นี้จัดการต่อการโทรแต่ละสาย ดังนั้น 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
        ) { 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)
        }
    }
ไอคอนการโทรขอรับการสนับสนุน
ไอคอนการโทรช่วยให้แอประบุไอคอนที่กำหนดเองซึ่งแสดงการโทรที่จะแสดงบนแพลตฟอร์มระยะไกลระหว่างการโทรได้ นอกจากนี้ ไอคอนนี้ยังอัปเดตได้ตลอดอายุการโทร
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)
        }
    }
