الاتصالات

تسهّل مكتبة Android Telecom Jetpack الجديدة إخبار النظام الأساسي بحالة مكالمتك. يمكنك العثور على رمز المصدر ونموذج تطبيق على GitHub.

التبعيات والأذونات

أولاً، افتح ملف Build.grale في وحدة التطبيق وأضِف تبعية لوحدة androidx Telecom:

dependencies {
    implementation ("androidx.core:core-telecom:1.0.0-alpha02")
}

في بيان التطبيق، يجب الإقرار بأنّ تطبيقك يستخدم إذن MANAGE_OWN_CALLS:

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

تسجيل الطلب

لإعلام Android بتطبيقك، عليك تسجيله وإمكاناته. يتيح هذا الإجراء لنظام Android تحديد الميزات التي يوفّرها تطبيقك، مثل مكالمات الفيديو وبث المكالمات وإجراء المكالمات. هذه المعلومات مهمة حتى يتمكن Android من تهيئة نفسه للعمل مع ميزات تطبيقك.

 private val callsManager = CallsManager(context)

var capabilities: @CallsManager.Companion.Capability Int =
    CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING

callsManager.registerAppWithTelecom(capabilities)

دمج النظام الأساسي

يُعد سيناريوهان الاتصال الأكثر شيوعًا لأي تطبيق اتصال هما المكالمات الواردة والصادرة. لتسجيل اتجاه المكالمة بشكل صحيح وإبلاغ المستخدم بالشكل المناسب من خلال الإشعارات، استخدم واجهات برمجة التطبيقات أدناه.

تسجيل مكالمة

يوضح هذا المثال كيفية تسجيل مكالمة واردة:

companion object {
  const val APP_SCHEME = "MyCustomScheme"
  const val ALL_CALL_CAPABILITIES = (CallAttributes.SUPPORTS_SET_INACTIVE
    or CallAttributes.SUPPORTS_STREAM or CallAttributes.SUPPORTS_TRANSFER)

  const val INCOMING_NAME = "Luke"
  val INCOMING_URI: Uri = Uri.fromParts(APP_SCHEME, "", "")
  // Define all possible properties for CallAttributes
  val INCOMING_CALL_ATTRIBUTES =
    CallAttributes(
      INCOMING_NAME,
      INCOMING_URI,
      DIRECTION_INCOMING,
      CALL_TYPE_VIDEO_CALL,
      ALL_CALL_CAPABILITIES)
}

يمكن أن يكون للعنصر callAttributes السمات التالية:

  • displayName: اسم المتصل أو الاجتماع أو الجلسة
  • address: عنوان المكالمة ملاحظة: يمكن إضافة رابط اجتماع.
  • direction: اتجاه المكالمة، مثل الواردة أو الصادرة.
  • callType: معلومات ذات صلة بالبيانات التي يتم نقلها، مثل الفيديو والصوت
  • callCapabilities: كائن يحدد إمكانات الاستدعاء.

يمكن أن يكون للعنصر callCapabilities السمات التالية:

  • streaming: يشير إلى ما إذا كانت المكالمة تتيح بث الصوت إلى جهاز آخر يعمل بنظام التشغيل Android.
  • transfer: يشير إلى ما إذا كان يمكن تحويل المكالمة أم لا.
  • hold: يشير إلى ما إذا كان يمكن تعليق المكالمة أم لا.

إضافة مكالمة

تعرض طريقة addCall() استثناءً إذا كان الجهاز لا يتوافق مع الاتصالات أو إذا حدث خطأ عند إعداد المكالمة.

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onIsCallAnswered, // Watch needs to know if it can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
        callControlScope = this
    }
}

الرد على مكالمة

ما إن تجري مكالمة واردة، يجب عليك الرد على المكالمة أو رفضها. يوضح هذا المثال كيفية الرد على مكالمة:

when (answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {

    }
}

إذا كانت هناك مكالمة أخرى قيد التقدّم، سيعرض "answer()" رسالة CallControlResult.Error، ما يوضّح سبب تعذّر الردّ على المكالمة. في هذه الحالة، يحتاج المستخدم إلى وضع المكالمة الأخرى قيد الانتظار.

رفض مكالمة

لرفض مكالمة، يمكنك إنهاء المكالمة باستخدام DisconnectCause.Rejected.

fun onRejectCall(){
    coroutineScope.launch {
        callControlScope?.let {
            it.disconnect(DisconnectCause(DisconnectCause.REJECTED))
        }
    }
}

مكالمة صادرة

عند إجراء مكالمة صادرة، وبعد رد الطرف البعيد، عليك ضبط المكالمة على نشطة لإعلام النظام الأساسي بأنّ المكالمة جارية:

when (setActive()) {
    is CallControlResult.Success -> {
        onIsCallActive()
    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

وضع مكالمة على الانتظار

إذا كان تطبيق الاتصال يتيح إجراء المكالمات، استخدِم setInActive لإعلام النظام الأساسي بأنّ مكالمتك غير نشطة وأنّ الميكروفون والكاميرا متاحان لاستخدام التطبيقات الأخرى:

when (setInActive()) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

قطع الاتصال

لإنهاء الاتصال، أبلغ حزمة الاتصالات بقطع الاتصال عن طريق تقديم سبب صالح:

coroutineScope.launch {
    callControlScope?.disconnect(DisconnectCause(DisconnectCause.LOCAL))
}

توجيه الصوت

أثناء المكالمة، يبدّل المستخدمون أحيانًا بين الأجهزة، مثل مكبّر صوت أو سماعة أذن أو جهاز يتضمّن بلوتوث. استخدِم واجهات برمجة التطبيقات availableEndpoints وcurrentCallEndpoint للحصول على قائمة بجميع الأجهزة المتاحة للمستخدم والأجهزة النشطة.

يدمج هذا المثال كلا التدفقين لإنشاء كائن واجهة المستخدم ليعرض للمستخدم قائمة بالأجهزة وأي منها هو نشط:

availableEndpoint = combine(callControlScope.availableEndpoints,
    callControlScope.currentCallEndpoint) {
    availableDevices: List<CallEndpoint>, activeDevice : CallEndpoint ->
    availableDevices.map {
        EndPointUI(
            isActive = activeDevice.endpointName == it.endpointName, it
        )
    }
}

لتغيير جهاز نشط، يمكنك استخدام requestEndpointChange مع CallEndpoint التي تريد التغيير إليها.

coroutineScope.launch {
     callControlScope?.requestEndpointChange(callEndpoint)
}

دعم التطبيقات التي تعمل في المقدّمة

تتوفّر مكتبة الاتصالات في المقدّمة. تستخدم هذه المكتبة ConnectionService على الأجهزة التي تعمل بنظام التشغيل Android 13 والإصدارات الأقدم. أما بالنسبة إلى نظام التشغيل Android 14 والإصدارات الأحدث، فهو يستخدم الميكروفون والكاميرا من نوع المقدمة لدعم الخدمات التي تعمل في المقدّمة بشكل صحيح. مزيد من المعلومات حول الخدمات التي تعمل في المقدّمة

كجزء من متطلبات المقدّمة، يجب أن ينشر التطبيق إشعارًا للمستخدمين ليعرفوا أنّ التطبيق قيد التشغيل في المقدّمة.

ولضمان حصول تطبيقك على أولوية التنفيذ التي يتم تنفيذها في المقدّمة، عليك إنشاء إشعار عند تسجيل المكالمة في النظام الأساسي. تتم إزالة الأولوية في المقدّمة عندما ينهي تطبيقك المكالمة أو عندما يصبح الإشعار صالحًا.

is TelecomCall.Registered -> {
    val notification = createNotification(call)
    notificationManager.notify(TELECOM_NOTIFICATION_ID, notification)
}

الحصول على الدعم

تحتوي الساعات على تطبيق عام لاستقبال نقطة النهاية. يوفر هذا التطبيق للمستخدم واجهة أساسية مثل الرد على المكالمات ورفضها وانفصالها. يدعم التطبيق هذه الإجراءات من خلال تنفيذ وظائف lambda التي تعلم النظام الأساسي بأنك قد نفّذت الإجراء على الجهاز.

تنتهي مهلة كل دالة lambda بعد 5 ثوانٍ بإخفاق المعاملة في حال عدم استجابة التطبيق.

callsManager.addCall(
        attributes,
        onIsCallAnswered, // Watch/Auto need to know if they can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
//Call Scope
}
/**
  *  Can the call be successfully answered??
  *  TIP: Check the connection/call state to see if you can answer a call
  *  Example you may need to wait for another call to hold.
  **/
val onIsCallAnswered: suspend(type: Int) -> Unit = {}

/**
  * Can the call perform a disconnect
  */
val onIsCallDisconnected: suspend (cause: DisconnectCause) -> Unit = {}

/**
  *  Check is see if you can make the call active.
  *  Other calls and state might stop us from activating the call
  */
val onIsCallActive: suspend () -> Unit = {
    updateCurrentCall {
    }
}

/**
  * Check to see if you can make the call inactivate
  */
val onIsCallInactive: suspend () -> Unit = {}