สร้างแอปพลิเคชันโทรศัพท์เริ่มต้น

แอปพลิเคชันโทรศัพท์เริ่มต้นช่วยให้เฟรมเวิร์กโทรคมนาคมของ Android แจ้งแอปพลิเคชันสถานะการโทรของคุณ โดยใช้ตัวจัดการบทบาทและบริการในสายเพื่อสร้างการแทนที่สำหรับแอปโทรศัพท์เริ่มต้นในอุปกรณ์ Android และใช้ InCallService API การติดตั้งใช้งานของคุณต้องเป็นไปตามข้อกำหนดต่อไปนี้

แอปต้องไม่มีความสามารถในการโทร และต้องประกอบด้วยอินเทอร์เฟซผู้ใช้สำหรับการโทรเพียงอย่างเดียว และต้องจัดการกับการเรียกใช้ทั้งหมดที่เฟรมเวิร์กโทรคมนาคมรับทราบ และไม่มีข้อสันนิษฐานเกี่ยวกับลักษณะของการโทร เช่น ต้องไม่ถือว่าการโทรเป็นการโทรแบบใช้ซิม และไม่ได้จำกัดการโทรที่อิงจาก ConnectionService ใดๆ เช่น การบังคับใช้ข้อจำกัดเกี่ยวกับโทรศัพท์สำหรับวิดีโอคอล

แอปการโทรช่วยให้ผู้ใช้รับสายหรือโทรด้วยเสียงหรือวิดีโอคอลใน อุปกรณ์ แอปการโทรใช้อินเทอร์เฟซผู้ใช้ของตนเองในการโทรแทนการใช้ อินเทอร์เฟซแอปโทรศัพท์เริ่มต้นดังที่แสดงในภาพหน้าจอต่อไปนี้

ตัวอย่างแอปการโทร
ตัวอย่างแอปการโทรที่ใช้อินเทอร์เฟซผู้ใช้ของตนเอง

เฟรมเวิร์ก Android มีแพ็กเกจ android.telecom ซึ่ง มีชั้นเรียนที่ช่วยคุณสร้างแอปโทรตามโทรคมนาคม การสร้างแอปของคุณตามเฟรมเวิร์กโทรคมนาคมมอบ ประโยชน์ดังต่อไปนี้

  • แอปของคุณทำงานร่วมกับระบบย่อยระบบโทรคมนาคมแบบเนทีฟได้อย่างถูกต้องใน อุปกรณ์
  • แอปของคุณทํางานร่วมกับแอปการโทรอื่นๆ ที่ปฏิบัติตามข้อกําหนดด้วยเช่นกันได้อย่างถูกต้อง กรอบการทำงาน
  • เฟรมเวิร์กนี้จะช่วยให้แอปจัดการการกำหนดเส้นทางเสียงและวิดีโอได้
  • เฟรมเวิร์กนี้ช่วยให้แอปทราบว่าการโทรมุ่งเน้นหรือไม่

การประกาศและสิทธิ์ในไฟล์ Manifest

ในไฟล์ Manifest ของแอป ให้ประกาศว่าแอปของคุณใช้ MANAGE_OWN_CALLS สิทธิ์ตามที่แสดงในตัวอย่างต่อไปนี้

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

ดูข้อมูลเพิ่มเติมเกี่ยวกับการประกาศสิทธิ์ของแอปได้ที่ สิทธิ์

คุณต้องประกาศบริการที่ระบุคลาสที่นำพารามิเตอร์ ConnectionService ชั้นเรียนในแอปของคุณ โทรคมนาคม ระบบย่อยกำหนดให้บริการต้องประกาศสิทธิ์ BIND_TELECOM_CONNECTION_SERVICE เชื่อมโยงกับโดเมนได้ ตัวอย่างต่อไปนี้แสดงวิธีประกาศบริการใน ไฟล์ Manifest ของแอป

<service android:name="com.example.MyConnectionService"
    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.ConnectionService" />
    </intent-filter>
</service>

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการประกาศคอมโพเนนต์ของแอป รวมถึงบริการ โปรดดู คอมโพเนนต์ของแอป

ใช้บริการการเชื่อมต่อ

แอปการโทรต้องติดตั้งใช้งานคลาส ConnectionService ที่เชื่อมโยงกับระบบย่อยโทรคมนาคม การใช้งาน ConnectionService ของคุณควรลบล้าง วิธีการต่อไปนี้

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

ระบบย่อยโทรคมนาคมเรียกวิธีนี้เพื่อตอบสนองต่อ แอปของคุณกำลังโทรหา placeCall(Uri, Bundle) เพื่อสร้างสายโทรออกใหม่ แอปของคุณแสดงผลอินสแตนซ์ใหม่ของการใช้งานคลาส Connection (ดูข้อมูลเพิ่มเติมได้ที่ ใช้การเชื่อมต่อ) เพื่อแสดง สายโทรออก คุณปรับแต่งการเชื่อมต่อขาออกเพิ่มเติมได้โดยทําดังนี้ การดำเนินการต่อไปนี้

  • แอปของคุณควรเรียกเมธอด setConnectionProperties(int) ด้วยค่าคงที่ PROPERTY_SELF_MANAGED เป็นอาร์กิวเมนต์ เพื่อระบุว่าการเชื่อมต่อมาจากแอปการโทร
  • หากแอปรองรับการพักสาย ให้เรียกใช้เมธอด setConnectionCapabilities(int) และตั้งค่า อาร์กิวเมนต์ค่าบิตมาสก์ของค่าคงที่ CAPABILITY_HOLD และ CAPABILITY_SUPPORT_HOLD
  • หากต้องการตั้งชื่อผู้โทร ให้ใช้วิธีการ setCallerDisplayName(String, int) ผ่านPRESENTATION_ALLOWED เป็นค่าคงที่เป็นพารามิเตอร์ int เพื่อระบุว่าชื่อผู้โทรควร ได้อีกด้วย
  • หากต้องการตรวจสอบว่าสายที่โทรออกมีสถานะวิดีโอที่เหมาะสม โปรดโทรหา setVideoState(int) ของออบเจ็กต์ Connection และส่งค่าที่แสดงผลโดย เมธอด getVideoState() ของ ConnectionRequest ออบเจ็กต์
onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

ระบบย่อยโทรคมนาคมจะเรียกวิธีนี้เมื่อแอปของคุณเรียกใช้เมธอด placeCall(Uri, Bundle) แต่การโทรออกไม่สามารถ ได้ เพื่อให้สอดคล้องกับสถานการณ์นี้ แอปของคุณควรแจ้งให้ผู้ใช้ทราบ (สำหรับ เช่น การใช้กล่องการแจ้งเตือนหรือข้อความโทสต์) ว่าไม่สามารถโทรออก วางแล้ว แอปของคุณอาจโทรออกไม่ได้หากมีการเชื่อมต่ออยู่ หมายเลขฉุกเฉิน หรือหากมีสายที่สนทนาอยู่ในแอปอื่นที่ไม่สามารถ พักสายก่อนโทรออก

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

ระบบย่อยโทรคมนาคมเรียกเมธอดนี้เมื่อแอปของคุณเรียกใช้เมธอด addNewIncomingCall(PhoneAccountHandle, Bundle) เพื่อแจ้งระบบเมื่อมีสายเรียกเข้าใหม่ในแอป แอปของคุณแสดงผล อินสแตนซ์ใหม่ของการติดตั้งใช้งาน Connection (สำหรับ ดูข้อมูลเพิ่มเติมที่ใช้การเชื่อมต่อ) เพื่อแสดงสายเรียกเข้าใหม่ คุณสามารถปรับแต่งการกำหนดค่า โดยดำเนินการดังต่อไปนี้

  • แอปของคุณควรเรียกเมธอด setConnectionProperties(int) ด้วยค่าคงที่ PROPERTY_SELF_MANAGED เป็นอาร์กิวเมนต์ เพื่อระบุว่าการเชื่อมต่อมาจากแอปการโทร
  • หากแอปรองรับการพักสาย ให้เรียกใช้เมธอด setConnectionCapabilities(int) และตั้งค่า อาร์กิวเมนต์ค่าบิตมาสก์ของค่าคงที่ CAPABILITY_HOLD และ CAPABILITY_SUPPORT_HOLD
  • หากต้องการตั้งชื่อผู้โทร ให้ใช้วิธีการ setCallerDisplayName(String, int) ผ่านPRESENTATION_ALLOWED เป็นค่าคงที่เป็นพารามิเตอร์ int เพื่อระบุว่าชื่อผู้โทรควร ได้อีกด้วย
  • หากต้องการระบุหมายเลขโทรศัพท์หรือที่อยู่ของสายเรียกเข้า ให้ใช้ เมธอด setAddress(Uri, int) ของออบเจ็กต์ Connection
  • หากต้องการตรวจสอบว่าสายที่โทรออกมีสถานะวิดีโอที่เหมาะสม โปรดโทรหา setVideoState(int) ของออบเจ็กต์ Connection และส่งค่าที่แสดงผลโดย เมธอด getVideoState() ของ ConnectionRequest ออบเจ็กต์
onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

ระบบย่อยโทรคมนาคมเรียกใช้เมธอดนี้เมื่อแอปของคุณเรียกใช้เมธอด addNewIncomingCall(PhoneAccountHandle, Bundle) เพื่อแจ้ง Telecom มีสายเรียกเข้าใหม่ แต่ไม่อนุญาตให้ใช้สายเรียกเข้า (สำหรับ โปรดดูข้อมูลที่หัวข้อข้อจำกัดในการโทร) แอปของคุณควร ปฏิเสธสายเรียกเข้าโดยไม่ส่งเสียง โดยคุณจะโพสต์การแจ้งเตือนเพื่อแจ้งข้อมูลได้ ผู้ใช้สายที่ไม่ได้รับ

ใช้การเชื่อมต่อ

แอปของคุณควรสร้างคลาสย่อยของ Connection เพื่อ แสดงถึงการโทรในแอปของคุณ คุณควรลบล้างเมธอดต่อไปนี้ใน การใช้งานของคุณ:

onShowIncomingCallUi()

ระบบย่อยโทรคมนาคมจะเรียกวิธีนี้เมื่อคุณเพิ่มสายเรียกเข้าใหม่ และ แอปของคุณควรแสดง UI ของสายเรียกเข้า

onCallAudioStateChanged(CallAudioState)

ระบบย่อยโทรคมนาคมเรียกวิธีการนี้เพื่อแจ้งแอปของคุณว่าเสียงที่ มีการเปลี่ยนแปลงเส้นทางหรือโหมด ซึ่งเรียกว่าเพื่อตอบสนองต่อแอปของคุณที่เปลี่ยน โหมดเสียงโดยใช้ setAudioRoute(int) อาจมีการเรียกวิธีการนี้ในกรณีที่ระบบเปลี่ยนเส้นทางเสียง (เช่น เมื่อชุดหูฟังบลูทูธยกเลิกการเชื่อมต่อ)

onHold()

ระบบย่อยโทรคมนาคมเรียกใช้เมธอดนี้เมื่อต้องการพักสาย ในการตอบสนองต่อคำขอนี้ แอปของคุณควรพักสายแล้วเรียกใช้ setOnHold() วิธีในการแจ้งให้ระบบทราบ ที่มีการระงับสายอยู่ ระบบย่อยโทรคมนาคมอาจเรียกใช้เมธอดนี้เมื่อ บริการระหว่างการโทร เช่น Android Auto ที่แสดงให้เห็นว่าการโทรต้องการ ส่งต่อคำขอของผู้ใช้ให้พักสาย ระบบย่อยโทรคมนาคมยังเรียก วิธีนี้ในกรณีที่ผู้ใช้เปิดใช้การโทรในแอปอื่น สำหรับข้อมูลเพิ่มเติม ข้อมูลเกี่ยวกับบริการในสาย โปรดดู InCallService

onUnhold()

ระบบย่อยโทรคมนาคมจะเรียกใช้เมธอดนี้เมื่อ อุปกรณ์ต้องการต่อสายที่พักสายอยู่ เมื่อแอปของคุณกลับมาทำงานอีกครั้งแล้ว ระบบควรเรียกใช้ setActive() เพื่อแจ้งให้ระบบทราบว่าไม่มีการพักสายแล้ว โทรคมนาคม ระบบย่อยอาจเรียกใช้วิธีการนี้เมื่อบริการในสาย เช่น Android Auto ที่แสดงว่าสายของคุณต้องการส่งต่อคำขอเพื่อโทรต่อ สำหรับ ดูข้อมูลเพิ่มเติมเกี่ยวกับบริการในสายได้ที่ InCallService

onAnswer()

ระบบย่อยโทรคมนาคมเรียกเมธอดนี้เพื่อแจ้ง แอปของคุณที่ควรรับสายเรียกเข้า เมื่อแอปตอบคำถามแล้ว ระบบควรเรียกใช้ setActive() เพื่อแจ้งระบบว่ามีการรับสายแล้ว โทรคมนาคม ระบบย่อยอาจเรียกวิธีการนี้เมื่อแอปของคุณเพิ่มสายเรียกเข้าใหม่และ มีสายที่สนทนาอยู่ในแอปอื่นอยู่แล้ว จึงไม่สามารถพักสายได้ ระบบย่อยโทรคมนาคมจะแสดง UI ของสายเรียกเข้าในนามของแอปของคุณใน อินสแตนซ์เหล่านี้ เฟรมเวิร์กมีวิธีการที่ทำงานมากเกินไปซึ่ง สนับสนุนเพื่อระบุสถานะวิดีโอที่จะรับสาย สำหรับข้อมูลเพิ่มเติม โปรดดูข้อมูลเพิ่มเติมที่ onAnswer(int)

onReject()

ระบบย่อยโทรคมนาคมเรียกเมธอดนี้เมื่อต้องการปฏิเสธสายเรียกเข้า การโทร เมื่อแอปปฏิเสธการเรียก แอปควรเรียก setDisconnected(DisconnectCause) และระบุ REJECTED เป็นพารามิเตอร์ แอปของคุณควร จากนั้นเรียกใช้เมธอด destroy() เพื่อแจ้ง ระบบที่แอปประมวลผลการโทร การโทรของระบบย่อยโทรคมนาคม วิธีนี้เมื่อผู้ใช้ปฏิเสธสายเรียกเข้าจากแอปของคุณ

onDisconnect()

ระบบย่อยโทรคมนาคมเรียกวิธีนี้เมื่อต้องการยกเลิกการเชื่อมต่อสาย เมื่อการโทรสิ้นสุดลง แอปควรเรียกเมธอด setDisconnected(DisconnectCause) และระบุ LOCAL เป็นพารามิเตอร์เพื่อระบุว่า คำขอของผู้ใช้ทำให้การโทรถูกตัดการเชื่อมต่อ จากนั้นแอปของคุณควรเรียกเมธอด destroy() วิธีในการแจ้งให้โทรคมนาคม ระบบย่อยที่แอปได้ประมวลผลการเรียก ระบบอาจเรียกใช้เมธอดนี้ เมื่อผู้ใช้ตัดการเชื่อมต่อการโทรผ่านบริการอื่นในสาย เช่น Android Auto นอกจากนี้ ระบบจะเรียกวิธีการนี้เมื่อการเรียกของคุณต้อง ยกเลิกการเชื่อมต่อ เพื่อให้สามารถโทรออกได้ เช่น หากผู้ใช้ต้องการ เพื่อโทรฉุกเฉิน ดูข้อมูลเพิ่มเติมเกี่ยวกับบริการระหว่างการโทรได้ที่ InCallService

รับมือกับสถานการณ์การโทรที่พบบ่อย

การใช้ ConnectionService API ในการโทร ต้องมีการโต้ตอบกับชั้นเรียนอื่นๆ ใน android.telecom ใหม่ ส่วนต่อไปนี้จะอธิบายสถานการณ์การโทรที่พบบ่อย และวิธี ต้องใช้ API ในการจัดการ

รับสายเรียกเข้า

ขั้นตอนในการจัดการสายเรียกเข้าจะเปลี่ยนไปหรือไม่ว่ามีสายโทรเข้าในแอปอื่นๆ หรือไม่ หรือไม่ เหตุผลที่กระบวนการทำงานแตกต่างกันคือ กรอบการทำงานของโทรคมนาคม ต้องสร้างข้อจำกัดบางอย่างเมื่อมีการเรียกใช้ในแอปอื่นๆ ดูแลให้แอปการโทรทั้งหมดในอุปกรณ์มีสภาพแวดล้อมที่เสถียร สำหรับข้อมูลเพิ่มเติม โปรดดูข้อมูลที่หัวข้อข้อจำกัดในการโทร

ไม่มีสายที่ใช้งานอยู่ในแอปอื่นๆ

หากต้องการรับสายเรียกเข้าเมื่อไม่มีสายที่ใช้งานอยู่ในแอปอื่นๆ ให้ทําตาม ขั้นตอนเหล่านี้:

  1. แอปของคุณได้รับสายเรียกเข้าใหม่โดยใช้กลไกปกติ
  2. ใช้เมธอด addNewIncomingCall(PhoneAccountHandle, Bundle) เพื่อ แจ้งระบบย่อยโทรคมนาคมเกี่ยวกับสายเรียกเข้าใหม่
  3. ระบบย่อยโทรคมนาคมเชื่อมโยงกับการใช้งาน ConnectionService ของแอปและขออินสแตนซ์ใหม่ ของคลาส Connection หมายถึงรายการเข้าใหม่ โดยใช้เมธอด onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
  4. ระบบย่อยโทรคมนาคมแจ้งให้แอปของคุณทราบว่าควรแสดงสายเรียกเข้า อินเทอร์เฟซผู้ใช้โดยใช้เมธอด onShowIncomingCallUi()
  5. แอปของคุณแสดง UI ที่เข้ามาใหม่โดยใช้การแจ้งเตือนที่มี Intent แบบเต็มหน้าจอ ดูข้อมูลเพิ่มเติมได้ที่ onShowIncomingCallUi()
  6. เรียกใช้เมธอด setActive() หากผู้ใช้ รับสายเรียกเข้า หรือ setDisconnected(DisconnectCause) ระบุ REJECTED เป็นพารามิเตอร์ตามด้วย ไปยังเมธอด destroy() หากผู้ใช้ ปฏิเสธสายเรียกเข้า

สายที่สนทนาอยู่ในแอปอื่นๆ ที่จะพักสายไม่ได้

เพื่อรับสายเรียกเข้าเมื่อมีสายที่สนทนาอยู่ในแอปอื่นๆ ซึ่งทำไม่ได้ ถูกระงับ โปรดทำตามขั้นตอนต่อไปนี้

  1. แอปของคุณได้รับสายเรียกเข้าใหม่โดยใช้กลไกปกติ
  2. ใช้เมธอด addNewIncomingCall(PhoneAccountHandle, Bundle) เพื่อ แจ้งระบบย่อยโทรคมนาคมเกี่ยวกับสายเรียกเข้าใหม่
  3. ระบบย่อยโทรคมนาคมเชื่อมโยงกับการใช้งาน ConnectionService ของแอปและขออินสแตนซ์ใหม่ ของออบเจ็กต์ Connection ที่แสดงถึง สายเรียกเข้าโดยใช้เมธอด onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
  4. ระบบย่อยโทรคมนาคมจะแสดง UI ของสายเรียกเข้าสำหรับสายเรียกเข้าของคุณ
  5. หากผู้ใช้รับสาย ระบบย่อยโทรคมนาคมจะเรียกใช้เมธอด onAnswer() คุณควรเรียกใช้เมธอด setActive() เพื่อระบุไปยังโทรคมนาคม ระบบย่อยที่เชื่อมต่อการโทรแล้ว
  6. หากผู้ใช้ปฏิเสธสาย ระบบย่อยโทรคมนาคมจะเรียกใช้เมธอด onReject() คุณควรเรียกเมธอด setDisconnected(DisconnectCause) โดยระบุ REJECTED เป็นพารามิเตอร์ ตามด้วย ไปยังเมธอด destroy()

โทรออก

ขั้นตอนการโทรออกจะต้องมีการจัดการกับความเป็นไปได้ที่ ไม่สามารถโทรออกได้เนื่องจากข้อจำกัดที่กำหนดโดยเฟรมเวิร์กโทรคมนาคม ดูข้อมูลเพิ่มเติมได้ที่ข้อจำกัดในการโทร

หากต้องการโทรออก ให้ทำตามขั้นตอนต่อไปนี้

  1. ผู้ใช้เริ่มโทรออกภายในแอปของคุณ
  2. ใช้เมธอด placeCall(Uri, Bundle) เพื่อแจ้ง ระบบย่อยเกี่ยวกับสายโทรออกใหม่ ข้อต่อไปนี้ ข้อควรพิจารณาสำหรับพารามิเตอร์เมธอด
    • พารามิเตอร์ Uri แสดงที่อยู่ที่มี กำลังโทรหา สำหรับหมายเลขโทรศัพท์ปกติ ให้ใช้ URI tel: สคีม
    • พารามิเตอร์ Bundle ช่วยให้คุณระบุข้อมูล เกี่ยวกับแอปการโทรโดยเพิ่มออบเจ็กต์ PhoneAccountHandle ของแอปในส่วนเพิ่มเติม EXTRA_PHONE_ACCOUNT_HANDLE บัญชี แอปต้องระบุออบเจ็กต์ PhoneAccountHandle ให้กับสายโทรออกทุกสาย
    • พารามิเตอร์ Bundle ยังให้คุณระบุได้ด้วยว่า สายที่โทรออกมีวิดีโอด้วยการระบุค่า STATE_BIDIRECTIONAL ในส่วนที่เกินมา EXTRA_START_CALL_WITH_VIDEO_STATE พิจารณาว่าโดยค่าเริ่มต้น ระบบย่อยโทรคมนาคมจะกำหนดเส้นทางวิดีโอไปยัง ลำโพง
  3. ระบบย่อยโทรคมนาคมเชื่อมโยงกับ ConnectionService ของแอป การใช้งานของคุณ
  4. หากแอปของคุณโทรออกไม่ได้ การโทรของระบบย่อยโทรคมนาคม เมธอด onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) เพื่อ แจ้งแอปของคุณว่าไม่สามารถโทรออกได้ในขณะนี้ แอปของคุณ ควรแจ้งให้ผู้ใช้ทราบว่าไม่สามารถโทรออกได้
  5. หากแอปของคุณโทรออกได้ ระบบย่อยของระบบโทรคมนาคม onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest) แอปควรแสดงผลอินสแตนซ์ของคลาส Connection เพื่อแสดงถึงสายโทรออกใหม่ สำหรับ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับพร็อพเพอร์ตี้ที่คุณควรตั้งค่าในการเชื่อมต่อ ดูใช้บริการเชื่อมต่อ
  6. เมื่อเชื่อมต่อสายโทรออกแล้ว ให้โทรหาเมธอด setActive() เพื่อแจ้งระบบย่อยของโทรคมนาคม การโทรทำงานอยู่

วางสาย

หากต้องการวางสาย ให้ทำตามขั้นตอนต่อไปนี้

  1. เรียก setDisconnected(DisconnectCause) ที่ส่ง LOCAL เป็นพารามิเตอร์หากผู้ใช้ วางสาย หรือส่ง REMOTE เป็นพารามิเตอร์หากคู่สนทนาสิ้นสุดการโทร
  2. เรียกใช้เมธอด destroy()

ข้อจำกัดในการโทร

เพื่อให้ผู้ใช้ของคุณได้รับประสบการณ์การโทรที่เรียบง่ายและสอดคล้องกัน โทรคมนาคม จะมีการบังคับใช้ข้อจำกัดบางประการสำหรับการจัดการการโทรในอุปกรณ์ สำหรับ เช่น ผู้ใช้ได้ติดตั้งแอปการโทร 2 แอป ConnectionService API ที่จัดการด้วยตนเอง, FooTalk และ BarTalk ในกรณีนี้ มีข้อจำกัดต่อไปนี้

  • สำหรับอุปกรณ์ที่ใช้ API ระดับ 27 หรือต่ำกว่า จะมีเพียงแอปเดียวที่สามารถรักษา การโทรที่ดำเนินอยู่ในช่วงเวลาใดเวลาหนึ่ง ข้อจำกัดนี้หมายความว่าในขณะที่ผู้ใช้มี การโทรที่ดำเนินอยู่โดยใช้แอป FooTalk แอป BarTalk ไม่สามารถเริ่มต้นหรือรับสายได้ การโทรใหม่

    ในอุปกรณ์ที่ใช้ API ระดับ 28 ขึ้นไป หากทั้ง FooTalk และ BarTalk ประกาศ CAPABILITY_SUPPORT_HOLD และ CAPABILITY_HOLD ผู้ใช้จะสามารถโทรออกได้มากกว่า 1 สายโดย สลับไปมาระหว่างแอปเพื่อเริ่มต้นหรือรับสายอื่น

  • หากผู้ใช้มีส่วนร่วมในการโทรที่มีการจัดการตามปกติ (เช่น การใช้ แอปโทรศัพท์หรือแอปโทรศัพท์ในตัว) ผู้ใช้จะต้องไม่อยู่ในสายที่มาจาก แอปการโทร ซึ่งหมายความว่าหากผู้ใช้อยู่ในสายปกติโดยใช้ ผู้ให้บริการเครือข่ายมือถือก็ไม่สามารถเข้าร่วม FooTalk หรือ BarTalk พร้อมกันได้ด้วยเช่นกัน

  • ระบบย่อยโทรคมนาคมจะตัดการเชื่อมต่อการโทรของแอปหากผู้ใช้กดหมายเลข โทรฉุกเฉิน

  • แอปของคุณจะไม่สามารถรับหรือโทรออกได้ในขณะที่ผู้ใช้อยู่ในสายฉุกเฉิน

  • หากมีสายที่สนทนาอยู่ในแอปการโทรอื่นเมื่อแอปรับสาย สายเรียกเข้า การรับสายเรียกเข้าจะวางสายที่สนทนาอยู่ แอปอื่น แอปของคุณไม่ควรแสดงอินเทอร์เฟซผู้ใช้สายเรียกเข้าตามปกติ เฟรมเวิร์กโทรคมนาคมจะแสดงอินเทอร์เฟซผู้ใช้เมื่อมีสายเรียกเข้า และแจ้งข้อมูล ผู้ใช้ที่รับสายใหม่จะวางสายที่สนทนาอยู่ ช่วงเวลานี้ หมายความว่าหากผู้ใช้อยู่ในสาย FooTalk และแอป BarTalk ได้รับ โครงสร้างโทรคมนาคมจะแจ้งให้ผู้ใช้ทราบว่ามี สายเรียกเข้าจาก BarTalk และการรับสาย BarTalk จะเป็นการวางสาย FooTalk โทร

การกลายเป็นแอปโทรศัพท์เริ่มต้น

แอปโทรศัพท์/แอปโทรศัพท์เริ่มต้นคือแอปซึ่งมีอินเทอร์เฟซผู้ใช้ขณะโทรขณะที่อุปกรณ์ อยู่ในสาย และยังเป็นวิธีเริ่มต้นการโทรและดูประวัติการโทรแก่ผู้ใช้อีกด้วย บนอุปกรณ์ของตน อุปกรณ์มาพร้อมกับแอปโทรศัพท์/แอปโทรศัพท์เริ่มต้นของระบบ ผู้ใช้ อาจเลือกแอปเดียวที่จะรับช่วงต่อจากแอประบบ แอปที่ต้องการ เติมเต็มบทบาทนี้จะใช้ RoleManager เพื่อขอให้กรอก บทบาทRoleManager.ROLE_DIALER

แอปโทรศัพท์เริ่มต้นจะมีอินเทอร์เฟซผู้ใช้ขณะที่อุปกรณ์กำลังอยู่ในสาย และอุปกรณ์ ไม่ได้อยู่ในโหมดใช้ในรถยนต์ (กล่าวคือ UiModeManager#getCurrentModeType() ไม่ได้อยู่ในโหมดรถยนต์ Configuration.UI_MODE_TYPE_CAR)

หากต้องการเติมเต็มบทบาท RoleManager.ROLE_DIALER แอปต้องเป็นไปตาม จำนวนข้อกำหนด

  • ต้องจัดการ Intent Intent#ACTION_DIAL ซึ่งหมายความว่าแอปจะต้องจัดเตรียม UI ของแป้นหมายเลขสำหรับให้ผู้ใช้เริ่มโทรออก
  • ต้องใช้ InCallService API อย่างสมบูรณ์และให้บริการทั้งสายเรียกเข้า UI รวมถึง UI ของการโทรที่ดำเนินอยู่

หมายเหตุ: หากแอปที่ป้อน RoleManager.ROLE_DIALER แสดงผล null InCallService ในระหว่างผูกมัด เฟรมเวิร์กโทรคมนาคมจะลดลงโดยอัตโนมัติ กลับไปใช้แอปโทรศัพท์ที่โหลดไว้ล่วงหน้าในอุปกรณ์ ระบบจะแสดงการแจ้งเตือนไปยัง เพื่อแจ้งให้ผู้ใช้ทราบว่าระบบยังคงโทรต่อโดยใช้แอปแป้นโทรศัพท์ที่โหลดไว้ให้ล่วงหน้า บัญชี แอปไม่ควรแสดงผลการเชื่อมโยง null การทำเช่นนี้หมายความว่า ข้อกำหนดของ RoleManager.ROLE_DIALER

หมายเหตุ: หากแอปเติมข้อมูลครบ RoleManager.ROLE_DIALER และทำการเปลี่ยนแปลงในเวลา ซึ่งทำให้ไม่เป็นไปตามข้อกำหนดของบทบาทนี้อีกต่อไป RoleManager จะนำแอปของคุณออกจากบทบาทโดยอัตโนมัติและปิด แอปของคุณ ตัวอย่างเช่น หากคุณใช้ PackageManager.setComponentEnabledSetting(ComponentName, int, int) ถึง ปิดใช้InCallServiceที่แอปประกาศในไฟล์ Manifest แบบเป็นโปรแกรม จะไม่ปฏิบัติตามข้อกำหนดที่คาดไว้ RoleManager.ROLE_DIALER

แป้นโทรศัพท์ที่โหลดล่วงหน้าจะถูกใช้งานทุกครั้งที่ผู้ใช้โทรฉุกเฉิน แม้ว่า ในแอปจะมีบทบาท RoleManager.ROLE_DIALER เพื่อให้ได้มาซึ่ง เมื่อโทรฉุกเฉิน แป้นโทรศัพท์เริ่มต้นควรใช้ "ทุกครั้ง" TelecomManager.placeCall(Uri, Bundle)เพื่อโทรออก (รวมถึง โทรฉุกเฉิน) วิธีนี้ช่วยให้แพลตฟอร์มยืนยันได้ว่าคำขอมาจากแหล่งที่มาดังกล่าว แป้นโทรศัพท์เริ่มต้น หากแอปโทรศัพท์ที่ไม่ได้โหลดไว้ล่วงหน้าใช้ Intent#ACTION_CALL เพื่อวาง การโทรฉุกเฉิน ระบบจะรับสายไปยังแอปแป้นโทรศัพท์ที่โหลดล่วงหน้าโดยใช้ Intent#ACTION_DIAL เพื่อการยืนยัน ทำให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดี

ด้านล่างคือตัวอย่างการลงทะเบียนไฟล์ Manifest สำหรับ InCallService ข้อมูลเมตา TelecomManager#METADATA_IN_CALL_SERVICE_UI ระบุว่า การใช้งาน InCallService มีวัตถุประสงค์เพื่อแทนที่ UI ในตัว ข้อมูลเมตา TelecomManager#METADATA_IN_CALL_SERVICE_RINGING บ่งบอกว่า InCallService จะเล่นเสียงเรียกเข้าเมื่อมีสายเข้า โปรดดู ด้านล่างเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับการแสดงสายเรียกเข้า UI และการเล่นเสียงเรียกเข้าในแอป

 <service android:name="your.package.YourInCallServiceImplementation"
          android:permission="android.permission.BIND_INCALL_SERVICE"
          android:exported="true">
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
          android:value="true" />
      <intent-filter>
          <action android:name="android.telecom.InCallService"/>
      </intent-filter>
 </service>

หมายเหตุ: คุณไม่ควรทำเครื่องหมาย InCallService ด้วยแอตทริบิวต์ android:exported="false"; การทำเช่นนั้นอาจส่งผลให้ไม่สามารถเชื่อมโยงกับการติดตั้งใช้งาน ระหว่างการโทร

นอกจากการใช้ InCallService API แล้ว คุณต้องประกาศกิจกรรมใน ไฟล์ Manifest ของคุณซึ่งจัดการ Intent Intent#ACTION_DIAL ตัวอย่างด้านล่างจะแสดง วิธีการทำงาน

 <activity android:name="your.package.YourDialerActivity"
           android:label="@string/yourDialerActivityLabel">
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
           <data android:scheme="tel" />
      </intent-filter>
 </activity>

เมื่อผู้ใช้ติดตั้งและเรียกใช้แอปพลิเคชันเป็นครั้งแรก คุณควรใช้ RoleManager เพื่อแจ้งให้ผู้ใช้ทราบว่าต้องการให้แอปของคุณดำเนินการหรือไม่ เป็นแอปโทรศัพท์เริ่มต้นแอปใหม่

โค้ดด้านล่างแสดงวิธีที่แอปของคุณสามารถขอเป็นแอปโทรศัพท์/แอปโทรศัพท์เริ่มต้นได้

 private static final int REQUEST_ID = 1;

 public void requestRole() {
     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
     Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
     startActivityForResult(intent, REQUEST_ID);
 }

 public void onActivityResult(int requestCode, int resultCode, Intent data) {
     if (requestCode == REQUEST_ID) {
         if (resultCode == android.app.Activity.RESULT_OK) {
             // Your app is now the default dialer app
         } else {
             // Your app is not the default dialer app
         }
     }
 }

สิทธิ์เข้าถึง InCallService สำหรับอุปกรณ์ที่สวมใส่ได้

    ถ้าแอปของคุณเป็นแอปที่ใช้ร่วมกันกับบุคคลที่สาม และต้องการเข้าถึง InCallService API ที่แอปสามารถทำได้มีดังนี้

    1. ประกาศสิทธิ์ MANAGE_ONGOING_CALLS ในไฟล์ Manifest
    2. เชื่อมโยงกับอุปกรณ์ที่สวมใส่ได้จริงผ่านทาง CompanionDeviceManager API เป็นแอปที่ใช้ร่วมกัน โปรดดูหัวข้อต่อไปนี้ https://developer.android.com/guide/topics/connectivity/Companion-device-pairing
    3. ใช้ InCallService นี้โดยใช้สิทธิ์ BIND_INCALL_SERVICE

การแสดงการแจ้งเตือนสายเรียกเข้า

เมื่อแอปของคุณได้รับสายเรียกเข้าใหม่ผ่าน InCallService#onCallAdded(Call) ซึ่งทำหน้าที่แสดง UI ของสายเรียกเข้า ซึ่งควรดำเนินการโดยใช้ NotificationManager API เพื่อโพสต์การแจ้งเตือนสายเรียกเข้าใหม่

ที่ที่แอปประกาศข้อมูลเมตา TelecomManager#METADATA_IN_CALL_SERVICE_RINGING มีหน้าที่เล่นเสียงเรียกเข้าสำหรับสายเข้า แอปของคุณควรสร้าง NotificationChannel ซึ่งระบุเสียงเรียกเข้าที่ต้องการ เช่น

 NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
          NotificationManager.IMPORTANCE_MAX);
 // other channel setup stuff goes here.

 // We'll use the default system ringtone for our incoming call notification channel.  You can
 // use your own audio resource here.
 Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
 channel.setSound(ringtoneUri, new AudioAttributes.Builder()
          // Setting the AudioAttributes is important as it identifies the purpose of your
          // notification sound.
          .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
      .build());

 NotificationManager mgr = getSystemService(NotificationManager.class);
 mgr.createNotificationChannel(channel);

เมื่อแอปได้รับสายเรียกเข้าใหม่ แอปจะสร้าง Notification สำหรับ สายเรียกเข้าและเชื่อมโยงกับช่องทางการแจ้งเตือนสายเรียกเข้า คุณสามารถระบุ PendingIntentในการแจ้งเตือนซึ่งจะเปิดโหมดเต็มหน้าจอของคุณ UI ของสายเรียกเข้า เฟรมเวิร์กเครื่องมือจัดการการแจ้งเตือนจะแสดงการแจ้งเตือนของคุณเป็น การแจ้งเตือนล่วงหน้าหากผู้ใช้กำลังใช้โทรศัพท์ เมื่อผู้ใช้ไม่ได้ใช้ ระบบจะใช้ UI ของสายเรียกเข้าแบบเต็มหน้าจอแทน เช่น

 // Create an intent which triggers your fullscreen incoming call user interface.
 Intent intent = new Intent(Intent.ACTION_MAIN, null);
 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
 intent.setClass(context, YourIncomingCallActivity.class);
 PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
 // Build the notification as an ongoing high priority item; this ensures it will show as
 // a heads up notification which slides down over top of the current content.
 final Notification.Builder builder = new Notification.Builder(context);
 builder.setOngoing(true);
 builder.setPriority(Notification.PRIORITY_HIGH);
 // Set notification content intent to take user to the fullscreen UI if user taps on the
 // notification body.
 builder.setContentIntent(pendingIntent);
 // Set full screen intent to trigger display of the fullscreen UI when the notification
 // manager deems it appropriate.
 builder.setFullScreenIntent(pendingIntent, true);
 // Setup notification content.
 builder.setSmallIcon( yourIconResourceId );
 builder.setContentTitle("Your notification title");
 builder.setContentText("Your notification content.");
 // Use builder.addAction(..) to add buttons to answer or reject the call.
 NotificationManager notificationManager = mContext.getSystemService(
     NotificationManager.class);
 notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
```