تسهّل مكتبة Core-Telecom عملية دمج تطبيق الاتصال مع نظام Android الأساسي من خلال توفير مجموعة قوية ومتّسقة من واجهات برمجة التطبيقات.
إذا أردت استكشاف عمليات التنفيذ العملية، يمكنك العثور على نماذج التطبيقات على GitHub:
- تطبيق خفيف الوزن كمثال: هو مثال بسيط
يوضّح كيفية استخدام واجهة برمجة التطبيقات
Core-Telecomوهو مثالي لفهم المفاهيم الأساسية بسرعة. - تطبيق شامل كمثال (تم تطويره من قِبل فريق Core-Telecom Team): هو تطبيق أكثر ثراءً بالميزات يعرض وظائف الاتصالات المتقدّمة وأفضل الممارسات. وهو مصدر رائع لفهم سيناريوهات التكامل المعقّدة.
إعداد 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 لإنشاء دورة حياة المكالمة وإدارتها.
إنشاء مكالمة
يحدّد العنصر 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))
إدارة نقاط نهاية الصوت للمكالمة
يمكنك مراقبة نقاط نهاية الصوت وإدارتها باستخدام currentCallEndpoint وavailableEndpoints وisMuted Flow ضمن 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 (المستوى 33 لواجهة برمجة التطبيقات Android 13 والإصدارات الأقدم) أو
foregroundtypes (المستوى 34 لواجهة برمجة التطبيقات Android 14 والإصدارات الأحدث) للدعم في المقدّمة.
كجزء من متطلبات المقدّمة، يجب أن ينشر التطبيق إشعارًا للمستخدمين لإعلامهم بأنّ التطبيق قيد التشغيل في المقدّمة.
لضمان حصول تطبيقك على أولوية التنفيذ في المقدّمة، أنشِئ إشعارًا بعد إضافة المكالمة باستخدام المنصة. تتم إزالة الأولوية في المقدّمة عندما ينهي تطبيقك المكالمة أو عندما يصبح إشعارك غير صالح.
مزيد من المعلومات عن الخدمات التي تعمل في المقدّمة.
دعم السطح عن بُعد
يمكن للأجهزة عن بُعد (الساعات الذكية وسماعات الرأس التي تعمل بالبلوتوث وAndroid Auto) إدارة المكالمات بدون تفاعل مباشر مع الهاتف. يجب أن ينفّذ تطبيقك تعبيرات لامدا لمعاودة الاتصال (onAnswerCall وonSetCallDisconnected وonSetCallActive وonSetCallInactive) المقدَّمة إلى CallsManager.addCall للتعامل مع الإجراءات التي تبدأها هذه الأجهزة.
عند حدوث إجراء عن بُعد، يتم استدعاء تعبير لامدا المقابل.
يشير إكمال تعبير لامدا بنجاح إلى أنّه تمّت معالجة الأمر. إذا تعذّر تنفيذ الأمر، يجب أن يطرح تعبير لامدا استثناءً.
يضمن التنفيذ السليم التحكّم السلس في المكالمات على مختلف الأجهزة. اختبِر بدقة باستخدام أسطح مختلفة عن بُعد.
إضافات المكالمات
بالإضافة إلى إدارة حالة المكالمة ومسار الصوت لمكالماتك، تتيح المكتبة أيضًا إضافات المكالمات، وهي ميزات اختيارية يمكن لتطبيقك تنفيذها للحصول على تجربة اتصال أفضل على مساحات العرض عن بُعد، مثل Android Auto. وتشمل هذه الميزات غرف الاجتماعات وتجاهل المكالمات ورموز المكالمات الإضافية. عندما ينفّذ تطبيقك إضافة، ستتم مزامنة المعلومات التي يقدّمها التطبيق مع جميع الأجهزة المتصلة التي تتيح أيضًا عرض هذه الإضافات في واجهة المستخدم. وهذا يعني أنّ هذه الميزات ستتوفّر أيضًا على الأجهزة عن بُعد ليتفاعل معها المستخدمون.
إنشاء مكالمة باستخدام الإضافات
عند إنشاء مكالمة، بدلاً من استخدام CallManager#addCall لإنشاء المكالمة،
يمكنك بدلاً من ذلك استخدام CallManager#addCallWithExtensions، ما يمنح
التطبيق إمكانية الوصول إلى نطاق مختلف يُسمى 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 للإعلان عن دعم هذه الإضافة واستخدِم واجهات برمجة التطبيقات ذات الصلة لتعديل الأسطح عن بُعد عند تغيير المشاركين.
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 تعقيد إدارة حالة الكتم العامة للمكالمات الخلوية الجارية أثناء تفعيل مكالمة عبر بروتوكول الصوت على الإنترنت. يقلّل ذلك من احتمالية حدوث أخطاء عند تجاهل الصوت الصادر في سيناريوهات المكالمات المتعددة، مع السماح أيضًا بميزات مفيدة مثل مؤشرات "هل تتحدث؟" عندما يتحدث المستخدم بدون أن يدرك أنّه تم تفعيل ميزة تجاهل المكالمات.
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 لتطبيقك بالتكامل مع سجلّ المكالمات في النظام. تتيح هذه الميزة ظهور المكالمات التي يتم إجراؤها أو تلقّيها باستخدام تطبيق بروتوكول الصوت على الإنترنت في سجلّ المكالمات في أداة الاتصال في النظام، ما يسمح للمستخدمين باستخدام أداة الاتصال لبدء معاودة الاتصال.
تفعيل التكامل مع سجلّ المكالمات
لتفعيل هذه الميزة، يجب أن يسجِّل تطبيقك نفسه للتعامل مع هدف
TelecomManager.ACTION_CALL_BACK في ملف AndroidManifest.xml وأن
يوفر Activity لمعالجة معاودة الاتصال. على سبيل المثال، لنفترض أنّ VoipCallbackActivity هو النشاط الذي يعالج معاودة الاتصال، يجب أن يبدو ملف البيان على النحو التالي:
تسجيل الهدف في ملف البيان
<activity
android:name=".VoipCallbackActivity"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.action.CALL_BACK" />
</intent-filter>
</activity>
معالجة هدف معاودة الاتصال
عندما يبدأ المستخدم معاودة الاتصال من أداة الاتصال في النظام، يتم تشغيل النشاط المسجَّل
باستخدام الإجراء TelecomManager.ACTION_CALL_BACK.
يحتوي الهدف على TelecomManager.EXTRA_UUID الذي تم تقديمه إلى تطبيقك عند
إضافة المكالمة في الأصل باستخدام CallsManager.
تعرِض الطريقة CallControlScope getCallId هذا المعرّف الفريد للـ
مكالمة.
عليك حفظ جميع المعلومات المطلوبة لبدء المكالمة التي تم تحديدها باستخدام المعرّف الفريد العالمي (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
)