Core-Telecom

کتابخانه Core-Telecom با ارائه مجموعه‌ای قوی و سازگار از APIها، فرآیند ادغام برنامه تماس شما با پلتفرم اندروید را ساده می‌کند.

اگر می‌خواهید پیاده‌سازی‌های عملی را بررسی کنید، می‌توانید نمونه‌هایی از برنامه‌ها را در GitHub پیدا کنید:

  • برنامه نمونه سبک - یک مثال مینیمال که نحوه استفاده از API Core-Telecom را نشان می‌دهد. ایده‌آل برای درک سریع مفاهیم اساسی.
  • نمونه برنامه جامع (توسعه یافته توسط تیم 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" />

اپلیکیشن خود را ثبت کنید

برای شروع افزودن تماس‌ها به سیستم، برنامه تماس خود را با استفاده از CallsManager در اندروید ثبت کنید. هنگام ثبت، قابلیت‌های برنامه خود را مشخص کنید (برای مثال، پشتیبانی از صدا و تصویر):

val callsManager = CallsManager(context)

val capabilities: @CallsManager.Companion.Capability Int =
    (CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)

callsManager.registerAppWithTelecom(capabilities)

مدیریت تماس

از APIهای Core-Telecom برای ایجاد و مدیریت چرخه عمر تماس استفاده کنید.

ایجاد تماس

شیء 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 و callbackها برای اضافه کردن یک فراخوانی جدید به سیستم و مدیریت به‌روزرسانی‌های سطح از راه دور استفاده کنید. 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 (اندروید ۱۳ API سطح ۳۳ و پایین‌تر) یا foregroundtypes (اندروید ۱۴ API سطح ۳۴ و بالاتر) استفاده می‌کند.

به عنوان بخشی از الزامات پیش‌زمینه، برنامه باید اعلانی را برای کاربران ارسال کند تا بدانند که برنامه در پیش‌زمینه اجرا می‌شود.

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

درباره خدمات پیش‌زمینه بیشتر بدانید .

پشتیبانی از راه دور سطح

دستگاه‌های از راه دور (ساعت‌های هوشمند، هدست‌های بلوتوث، اندروید اتو) قادر به مدیریت تماس بدون تعامل مستقیم با تلفن هستند. برنامه شما باید لامبداهای callback ( onAnswerCall ، onSetCallDisconnected ، onSetCallActive ، onSetCallInactive ) ارائه شده به CallsManager.addCall را پیاده‌سازی کند تا اقدامات آغاز شده توسط این دستگاه‌ها را مدیریت کند.

وقتی یک عمل از راه دور رخ می‌دهد، لامبدا مربوطه فراخوانی می‌شود.

تکمیل موفقیت‌آمیز لامبدا نشان می‌دهد که دستور پردازش شده است. اگر دستور قابل اجرا نباشد، لامبدا باید یک استثنا ایجاد کند.

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

افزونه‌های تماس

این کتابخانه علاوه بر مدیریت وضعیت تماس و مسیر صوتی تماس‌های شما، از افزونه‌های تماس نیز پشتیبانی می‌کند، که ویژگی‌های اختیاری هستند که برنامه شما می‌تواند برای تجربه تماس غنی‌تر در سطوح دوردست، مانند Android Auto، پیاده‌سازی کند. این ویژگی‌ها شامل اتاق‌های جلسه، بی‌صدا کردن تماس و نمادهای تماس اضافی است. هنگامی که برنامه شما یک افزونه را پیاده‌سازی می‌کند، اطلاعاتی که برنامه ارائه می‌دهد با تمام دستگاه‌های متصل که از نمایش این افزونه‌ها در رابط کاربری خود پشتیبانی می‌کنند، همگام‌سازی می‌شود. این بدان معناست که این ویژگی‌ها در دستگاه‌های دوردست نیز برای تعامل کاربران در دسترس خواهند بود.

ایجاد تماس با افزونه‌ها

هنگام ایجاد یک فراخوانی، به جای استفاده از CallManager#addCall برای ایجاد فراخوانی، می‌توانید از CallManager#addCallWithExtensions استفاده کنید که به برنامه امکان دسترسی به یک scope متفاوت به نام ExtensionInitializationScope را می‌دهد. این scope به برنامه اجازه می‌دهد تا مجموعه‌ای از extensionهای اختیاری را که پشتیبانی می‌کند، مقداردهی اولیه کند. علاوه بر این، این scope یک متد اضافی به onCall ارائه می‌دهد که پس از تبادل قابلیت extension و تکمیل مقداردهی اولیه، یک 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)
        }
    }

پشتیبانی از بی‌صدا کردن تماس

بی‌صدا کردن تماس به کاربر اجازه می‌دهد تا از برنامه بخواهد صدای خروجی تماس را بی‌صدا کند، بدون اینکه میکروفون دستگاه را به صورت فیزیکی بی‌صدا کند. این ویژگی به ازای هر تماس مدیریت می‌شود، بنابراین جت‌پک پیچیدگی مدیریت حالت بی‌صدا کردن سراسری تماس‌های سلولی در حال انجام را در حالی که یک تماس 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)
        }
    }

ادغام با گزارش تماس سیستم

Core-Telecom به برنامه شما اجازه می‌دهد تا با گزارش تماس سیستم ادغام شود. این ویژگی باعث می‌شود تماس‌های برقرار شده یا دریافت شده با استفاده از برنامه VoIP شما در تاریخچه تماس شماره‌گیر سیستم نمایش داده شوند و به کاربران امکان می‌دهد از شماره‌گیر برای شروع تماس مجدد استفاده کنند.

فعال کردن ادغام گزارش تماس

برای فعال کردن این ویژگی، برنامه شما باید برای مدیریت TelecomManager.ACTION_CALL_BACK intent در AndroidManifest.xml خود ثبت نام کند و یک Activity برای پردازش callback ارائه دهد. برای مثال، فرض کنید VoipCallbackActivity activity ای باشد که callback را مدیریت می‌کند، در این صورت manifest شما باید به شکل زیر باشد:

ثبت اینتنت در مانیفست

<activity
    android:name=".VoipCallbackActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.telecom.action.CALL_BACK" />
    </intent-filter>
</activity>

مدیریت قصد فراخوانی مجدد

وقتی کاربر از طریق شماره‌گیر سیستم، فراخوانی مجدد (callback) را آغاز می‌کند، فعالیت ثبت‌شده‌ی شما با اکشن TelecomManager.ACTION_CALL_BACK اجرا می‌شود. اینتنت شامل TelecomManager.EXTRA_UUID است که هنگام اضافه کردن اولیه‌ی فراخوانی با استفاده از CallsManager به برنامه‌ی شما ارائه شده است.

متد getCallId CallControlScope این شناسه منحصر به فرد را برای فراخوانی برمی‌گرداند.

شما باید تمام اطلاعات مورد نیاز برای شروع تماس شناسایی شده توسط UUID را در داخل برنامه خود ذخیره کنید (برای مثال، لیستی از شماره تلفن‌ها برای یک تماس گروهی) تا بتوانید هنگام فراخوانی مجدد، تماس را دوباره بازسازی و برقرار کنید.

class VoipCallbackActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (intent?.action == TelecomManager.ACTION_CALL_BACK) {
            val uuidString = intent.getStringExtra(TelecomManager.EXTRA_UUID)
            if (uuidString != null) {
                val uuid = UUID.fromString(uuidString)
                
                // Retrieve your persisted call info using the UUID
                val callData = CallRepository.getCallByUuid(uuid)
                
                if (callData != null) {
                    // Place the call again using your app's logic
                    // ...
                }
            }
        }
        finish()
    }
}

انصراف از ادغام گزارش تماس

به طور پیش‌فرض، تماس‌ها در گزارش تماس سیستم ثبت می‌شوند. می‌توانید با تنظیم پارامتر isLogged به false هنگام ایجاد CallAttributesCompat ، این ویژگی را برای هر تماس غیرفعال کنید:

val callAttributes = CallAttributesCompat(
    displayName = callerName,
    address = addressUri,
    direction = CallAttributesCompat.DIRECTION_OUTGOING,
    isLogExcluded = true // Opt-out of system call logging
)