ภาพรวมเฟรมเวิร์กโทรคมนาคม

เฟรมเวิร์ก Android Telecom (หรือเรียกง่ายๆ ว่า "Telecom") จะจัดการการโทรด้วยเสียงและวิดีโอคอลในอุปกรณ์ที่ใช้ Android ซึ่งรวมถึงการโทรผ่านซิม เช่น การโทรที่ใช้เฟรมเวิร์กโทรศัพท์ และการโทรผ่าน VoIP ที่ใช้ไลบรารี Core-Telecom Jetpack

คอมโพเนนต์หลักที่ Telecom จัดการคือ ConnectionService และ InCallService

การติดตั้งใช้งาน ConnectionService สร้างขึ้นจากเทคโนโลยีต่างๆ เช่น PSTN เพื่อเชื่อมต่อการโทรไปยังบุคคลอื่น การใช้งาน ConnectionService ที่พบบ่อยที่สุดในโทรศัพท์คือ ConnectionService โทรศัพท์ ซึ่งจะเชื่อมต่อการโทรผ่านเครือข่ายของผู้ให้บริการ

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

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

คุณอาจต้องติดตั้งใช้งาน Telecom API ด้วยเหตุผลต่อไปนี้

สร้างแอปโทรศัพท์เปลี่ยนทดแทน

หากต้องการสร้างแอปโทรศัพท์เริ่มต้นทดแทนในอุปกรณ์ Android ให้ติดตั้งใช้งาน InCallService API การติดตั้งใช้งานต้องเป็นไปตามข้อกําหนดต่อไปนี้

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

ดูข้อมูลเพิ่มเติมได้ที่ InCallService

ผสานรวมโซลูชันการโทร

หากต้องการผสานรวมโซลูชันการโทรเข้ากับ Android คุณมีตัวเลือกต่อไปนี้

  • ใช้คลัง Core-Telecom Jetpack ที่จัดการด้วยตนเอง:ตัวเลือกนี้เหมาะสำหรับนักพัฒนาแอปการโทรแบบสแตนด์อโลนที่ไม่ต้องการแสดงการโทรภายในแอปโทรศัพท์เริ่มต้น และไม่ต้องการให้แสดงการโทรอื่นๆ ในอินเทอร์เฟซผู้ใช้

    เมื่อผสานรวมกับไลบรารี Core-Telecom Jetpack คุณจะทําให้แอปทํางานร่วมกันได้ไม่เพียงกับการโทรผ่านระบบโทรศัพท์ในอุปกรณ์เท่านั้น แต่ยังรวมถึงแอปการโทรแบบสแตนด์อโลนอื่นๆ ที่ผสานรวมกับ Telecom ด้วย ไลบรารี Core-Telecom ยังจัดการการกำหนดเส้นทางและโฟกัสเสียงด้วย โปรดดูรายละเอียดที่หัวข้อสร้างแอปการโทร

  • ใช้ ConnectionService API ที่มีการจัดการ:ตัวเลือกนี้ช่วยอำนวยความสะดวกในการพัฒนาโซลูชันการโทรที่ใช้แอปพลิเคชันโทรศัพท์ของอุปกรณ์ที่มีอยู่เพื่อแสดงอินเทอร์เฟซผู้ใช้สำหรับการโทร ตัวอย่างเช่น การใช้งานบริการโทรผ่าน SIP และ VoIP ของบุคคลที่สาม ดูรายละเอียดเพิ่มเติมได้ที่ getDefaultDialerPackage()

    ConnectionService เพียงอย่างเดียวมีไว้เพื่อเชื่อมต่อการโทรเท่านั้น โดยไม่มีอินเทอร์เฟซผู้ใช้ที่เชื่อมโยง

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

สกรีนสายเรียกเข้า

อุปกรณ์ที่ใช้ Android 10 (API ระดับ 29) ขึ้นไปอนุญาตให้แอปของคุณระบุการโทรจากหมายเลขที่ไม่ได้อยู่ในสมุดที่อยู่ของผู้ใช้ว่าเป็นโทรสแปมได้ ผู้ใช้เลือกให้ปฏิเสธสายสแปมโดยอัตโนมัติได้ ระบบจะบันทึกข้อมูลเกี่ยวกับการโทรที่ถูกบล็อกเหล่านี้ไว้ในบันทึกการโทรเพื่อให้ผู้ใช้มีความโปร่งใสมากขึ้นเมื่อพลาดสาย การใช้ Android 10 API จะทำให้คุณไม่ต้องขอสิทธิ์ READ_CALL_LOG จากผู้ใช้เพื่อใช้ฟังก์ชันการทำงานต่างๆ เช่น การสกรีนสายเรียกเข้าและการแสดงหมายเลขผู้โทร

คุณใช้การติดตั้งใช้งาน CallScreeningService เพื่อสกรีนสายเรียกเข้า เรียกใช้ฟังก์ชัน onScreenCall() สำหรับสายเรียกเข้าหรือสายโทรออกใหม่เมื่อหมายเลขดังกล่าวไม่ได้อยู่ในรายชื่อติดต่อของผู้ใช้ คุณสามารถตรวจสอบข้อมูลเกี่ยวกับการโทรได้จากออบเจ็กต์ Call.Details กล่าวโดยละเอียดคือ ฟังก์ชัน getCallerNumberVerificationStatus() จะรวมข้อมูลจากผู้ให้บริการเครือข่ายเกี่ยวกับหมายเลขอื่น หากสถานะการยืนยันไม่สำเร็จ แสดงว่าสายนั้นโทรมาจากหมายเลขที่ไม่ถูกต้องหรืออาจเป็นสายสแปม

Kotlin

class ScreeningService : CallScreeningService() {
    // This function is called when an ingoing or outgoing call
    // is from a number not in the user's contacts list
    override fun onScreenCall(callDetails: Call.Details) {
        // Can check the direction of the call
        val isIncoming = callDetails.callDirection == Call.Details.DIRECTION_INCOMING

        if (isIncoming) {
            // the handle (e.g. phone number) that the Call is currently connected to
            val handle: Uri = callDetails.handle

            // determine if you want to allow or reject the call
            when (callDetails.callerNumberVerificationStatus) {
                Connection.VERIFICATION_STATUS_FAILED -> {
                    // Network verification failed, likely an invalid/spam call.
                }
                Connection.VERIFICATION_STATUS_PASSED -> {
                    // Network verification passed, likely a valid call.
                }
                else -> {
                    // Network could not perform verification.
                    // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED.
                }
            }
        }
    }
}

Java

class ScreeningService extends CallScreeningService {
    @Override
    public void onScreenCall(@NonNull Call.Details callDetails) {
        boolean isIncoming = callDetails.getCallDirection() == Call.Details.DIRECTION_INCOMING;

        if (isIncoming) {
            Uri handle = callDetails.getHandle();

            switch (callDetails.getCallerNumberVerificationStatus()) {
                case Connection.VERIFICATION_STATUS_FAILED:
                    // Network verification failed, likely an invalid/spam call.
                    break;
                case Connection.VERIFICATION_STATUS_PASSED:
                    // Network verification passed, likely a valid call.
                    break;
                default:
                    // Network could not perform verification.
                    // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED
            }
        }
    }
}

ตั้งค่าฟังก์ชัน onScreenCall() ให้เรียกใช้ respondToCall() เพื่อบอกให้ระบบทราบวิธีตอบสนองต่อการโทรใหม่ ฟังก์ชันนี้ใช้พารามิเตอร์ CallResponse ที่คุณสามารถใช้เพื่อบอกให้ระบบบล็อกสาย ปฏิเสธสายราวกับว่าผู้ใช้เป็นคนปฏิเสธ หรือปิดเสียงสาย นอกจากนี้ คุณยังบอกให้ระบบข้ามการเพิ่มการโทรนี้ลงในบันทึกการโทรของอุปกรณ์ไปเลยได้

Kotlin

// Tell the system how to respond to the incoming call
// and if it should notify the user of the call.
val response = CallResponse.Builder()
    // Sets whether the incoming call should be blocked.
    .setDisallowCall(false)
    // Sets whether the incoming call should be rejected as if the user did so manually.
    .setRejectCall(false)
    // Sets whether ringing should be silenced for the incoming call.
    .setSilenceCall(false)
    // Sets whether the incoming call should not be displayed in the call log.
    .setSkipCallLog(false)
    // Sets whether a missed call notification should not be shown for the incoming call.
    .setSkipNotification(false)
    .build()

// Call this function to provide your screening response.
respondToCall(callDetails, response)

Java

// Tell the system how to respond to the incoming call
// and if it should notify the user of the call.
CallResponse.Builder response = new CallResponse.Builder();
// Sets whether the incoming call should be blocked.
response.setDisallowCall(false);
// Sets whether the incoming call should be rejected as if the user did so manually.
response.setRejectCall(false);
// Sets whether ringing should be silenced for the incoming call.
response.setSilenceCall(false);
// Sets whether the incoming call should not be displayed in the call log.
response.setSkipCallLog(false);
// Sets whether a missed call notification should not be shown for the incoming call.
response.setSkipNotification(false);

// Call this function to provide your screening response.
respondToCall(callDetails, response.build());

คุณต้องลงทะเบียนการใช้งาน CallScreeningService ในไฟล์ Manifest ด้วยตัวกรอง Intent และสิทธิ์ที่เหมาะสมเพื่อให้ระบบทริกเกอร์ได้อย่างถูกต้อง

<service
    android:name=".ScreeningService"
    android:permission="android.permission.BIND_SCREENING_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.CallScreeningService" />
    </intent-filter>
</service>

เปลี่ยนเส้นทางการโทร

อุปกรณ์ที่ใช้ Android 10 ขึ้นไปจะจัดการความตั้งใจโทรแตกต่างจากอุปกรณ์ที่ใช้ Android 9 หรือต่ำกว่า ใน Android 10 ขึ้นไป ระบบจะเลิกใช้งาน ACTION_NEW_OUTGOING_CALL broadcasting แล้วแทนที่ด้วย CallRedirectionService API CallRedirectionService มีอินเทอร์เฟซให้คุณใช้เพื่อแก้ไขการโทรออกที่แพลตฟอร์ม Android ดำเนินการ เช่น แอปของบุคคลที่สามอาจยกเลิกการโทรและเปลี่ยนเส้นทางการโทรผ่าน VoIP

Kotlin

class RedirectionService : CallRedirectionService() {
    override fun onPlaceCall(
        handle: Uri,
        initialPhoneAccount: PhoneAccountHandle,
        allowInteractiveResponse: Boolean
    ) {
        // Determine if the call should proceed, be redirected, or cancelled.
        val callShouldProceed = true
        val callShouldRedirect = false
        when {
            callShouldProceed -> {
                placeCallUnmodified()
            }
            callShouldRedirect -> {
                // Update the URI to point to a different phone number or modify the
                // PhoneAccountHandle and redirect.
                redirectCall(handle, initialPhoneAccount, true)
            }
            else -> {
                cancelCall()
            }
        }
    }
}

Java

class RedirectionService extends CallRedirectionService {
    @Override
    public void onPlaceCall(
            @NonNull Uri handle,
            @NonNull PhoneAccountHandle initialPhoneAccount,
            boolean allowInteractiveResponse
    ) {
        // Determine if the call should proceed, be redirected, or cancelled.
        // Your app should implement this logic to determine the redirection.
        boolean callShouldProceed = true;
        boolean callShouldRedirect = false;
        if (callShouldProceed) {
            placeCallUnmodified();
        } else if (callShouldRedirect) {
            // Update the URI to point to a different phone number or modify the
            // PhoneAccountHandle and redirect.
            redirectCall(handle, initialPhoneAccount, true);
        } else {
            cancelCall();
        }
    }
}

คุณต้องลงทะเบียนบริการนี้ในไฟล์ Manifest เพื่อให้ระบบเริ่มต้นบริการได้อย่างถูกต้อง

<service
    android:name=".RedirectionService"
    android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.CallRedirectionService"/>
    </intent-filter>
</service>

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

คุณควรตรวจสอบว่าแอปมีบทบาทนี้เมื่อผู้ใช้เปิดแอปของคุณหรือไม่ เพื่อให้ขอบทบาทดังกล่าวได้หากจำเป็น คุณเปิดใช้งาน Intent ที่ RoleManager สร้างขึ้น ดังนั้นอย่าลืมลบล้างฟังก์ชัน onActivityResult() เพื่อจัดการการเลือกของผู้ใช้

Kotlin

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Tell the system that you want your app to handle call redirects. This
        // is done by using the RoleManager to register your app to handle redirects.
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            val roleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager
            // Check if the app needs to register call redirection role.
            val shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) &&
                    !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION)
            if (shouldRequestRole) {
                val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION)
                startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE)
            }
        }
    }

    companion object {
        private const val REDIRECT_ROLE_REQUEST_CODE = 1
    }
}

Java

class MainActivity extends AppCompatActivity {
    private static final int REDIRECT_ROLE_REQUEST_CODE = 0;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Tell the system that you want your app to handle call redirects. This
        // is done by using the RoleManager to register your app to handle redirects.
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE);
            // Check if the app needs to register call redirection role.
            boolean shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) &&
                    !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION);
            if (shouldRequestRole) {
                Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION);
                startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE);
            }
        }
    }
}