نمای کلی چارچوب Telecom

چارچوب Android Telecom (که به‌طور ساده «Telecom» نیز نامیده می‌شود) تماس‌های صوتی و ویدیویی را در دستگاه‌های مجهز به Android مدیریت می‌کند. این شامل تماس‌های مبتنی بر سیم‌کارت، مانند تماس‌هایی که از چارچوب تلفنی استفاده می‌کنند، و تماس‌های VoIP که کتابخانه Core-Telecom Jetpack را پیاده‌سازی می‌کنند می‌شود.

بخش‌های اصلی که Telecom مدیریت می‌کند ConnectionService و InCallService هستند.

پیاده‌سازی ConnectionService بر فناوری‌هایی مانند PSTN برای اتصال تماس‌ها به طرف‌های دیگر تکیه دارد. رایج‌ترین پیاده‌سازی ConnectionService در تلفن، ConnectionService تلفنی است. این برنامه تماس‌های شرکت مخابراتی را متصل می‌کند.

پیاده‌سازی InCallService میانای کاربری برای تماس‌های مدیریت‌شده توسط Telecom ارائه می‌دهد و به کاربر اجازه می‌دهد این تماس‌ها را کنترل کند و با آن‌ها تعامل داشته باشد. رایج‌ترین پیاده‌سازی InCallService برنامه تلفنی است که با دستگاه همراه است.

Telecom به‌عنوان یک مرکز تلفن عمل می‌کند. این API تماس‌هایی را که پیاده‌سازی‌های ConnectionService ارائه می‌دهند به میاناهای کاربر تماس‌گیرنده‌ای که پیاده‌سازی‌های InCallService ارائه می‌دهند هدایت می‌کند.

ممکن است بخواهید میاناهای برنامه‌سازی کاربردی Telecom را به‌دلایل زیر پیاده‌سازی کنید:

ایجاد کردن برنامه تلفن جایگزین

برای ایجاد جایگزینی برای برنامه تلفن پیش‌فرض در دستگاه Android، InCallService API را پیاده‌سازی کنید. پیاده‌سازی شما باید الزامات زیر را برآورده کند:

  • نباید هیچ‌گونه قابلیت تماسی داشته باشد و باید صرفاً شامل میانای کاربر برای تماس باشد.
  • این برنامه باید همه تماس‌هایی را که چارچوب Telecom از آن‌ها مطلع است مدیریت کند و درباره ماهیت تماس‌ها فرضیه‌ای نسازد. برای مثال، نباید فرض کند تماس‌ها از نوع تماس‌های تلفنی مبتنی بر سیم‌کارت هستند، یا محدودیت‌های تماسی را که مبتنی بر هریک از 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 نسخه ۱۰ (سطح میانای برنامه‌سازی کاربردی ۲۹) یا بالاتر را اجرا می‌کنند به برنامه شما اجازه می‌دهند تماس‌های دریافتی از شماره‌هایی را که در دفترچه تلفن کاربر نیستند به‌عنوان تماس‌های هرزنامه احتمالی شناسایی کند. کاربران می‌توانند انتخاب کنند که تماس‌های هرزنامه بی‌صدا رد شوند. برای ارائه شفافیت بیشتر به کاربران هنگام ازدست دادن تماس‌ها، اطلاعات مربوط به این تماس‌های مسدودشده در گزارش تماس ثبت می‌شود. استفاده از Android 10 API نیاز به دریافت اجازه READ_CALL_LOG از کاربر را برای ارائه عملکرد غربال تماس و شناسه تماس‌گیرنده ازبین می‌برد.

از پیاده‌سازی CallScreeningService برای غربال کردن تماس‌ها استفاده می‌کنید. تابع onScreenCall() را برای هر تماس ورودی یا خروجی جدیدی که شماره آن در فهرست مخاطبین کاربر نیست فراخوانی کنید. می‌توانید شیء Call.Details را برای اطلاعات درباره تماس بررسی کنید. به‌طور مشخص، getCallerNumberVerificationStatus() تابع شامل اطلاعاتی از ارائه‌دهنده شبکه درباره شماره دیگر است. اگر وضعیت درستی‌سنجی ناموفق باشد، این نشانه خوبی است که تماس از شماره‌ای نامعتبر یا تماس هرزنامه احتمالی است.

کاتلین

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.
                }
            }
        }
    }
}

جاوا

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 را می‌گیرد که می‌توانید از آن برای دستور دادن به سیستم برای مسدود کردن تماس، رد کردن آن (به‌گونه‌ای که انگار کاربر آن را رد کرده است)، یا بی‌صدا کردن آن استفاده کنید. همچنین می‌توانید به سیستم بگویید که این تماس را کلاً به گزارش تماس دستگاه اضافه نکند.

کاتلین

// 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)

جاوا

// 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 را در فایل مانیفست با فیلتر قصد و اجازه مناسب ثبت کنید تا سیستم بتواند آن را به‌درستی راه‌اندازی کند.

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

هدایت کردن تماس

دستگاه‌هایی که Android نسخه ۱۰ یا بالاتر را اجرا می‌کنند، اهداف تماس را متفاوت از دستگاه‌هایی که Android نسخه ۹ یا پایین‌تر را اجرا می‌کنند مدیریت می‌کنند. در Android 10 و بالاتر، ACTION_NEW_OUTGOING_CALL منسوخ شده است و با CallRedirectionService API جایگزین شده است. CallRedirectionService میاناهایی را برای شما فراهم می‌کند تا از آن‌ها برای اصلاح تماس‌های خروجی برقرارشده توسط پلاتفرم Android استفاده کنید. برای مثال، برنامه‌های طرف سوم ممکن است تماس‌ها را لغو کنند و آن‌ها را ازطریق VoIP تغییر مسیر دهند.

کاتلین

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()
            }
        }
    }
}

جاوا

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();
        }
    }
}

باید این سرویس را در مانیفست ثبت کنید تا سیستم بتواند آن را به‌درستی راه‌اندازی کند.

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

برای استفاده از سرویس هدایت، برنامه‌تان باید نقش هدایت تماس را از RoleManager درخواست کند. این کار از کاربر می‌پرسد که آیا می‌خواهد به برنامه شما اجازه دهد تغییر مسیر تماس را مدیریت کند. اگر این نقش به برنامه‌تان اعطا نشود، از سرویس تغییر مسیر شما استفاده نمی‌شود.

وقتی کاربر برنامه‌تان را راه‌اندازی می‌کند، باید بررسی کنید که آیا برنامه‌تان این نقش را دارد یا نه تا بتوانید درصورت نیاز آن را درخواست کنید. قصدی را که توسط RoleManager ایجاد شده است راه‌اندازی می‌کنید، بنابراین مطمئن شوید که تابع onActivityResult() را لغو می‌کنید تا انتخاب کاربر را مدیریت کنید.

کاتلین

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
    }
}

جاوا

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);
            }
        }
    }
}