إنشاء تطبيق هاتف افتراضي

يسمح تطبيق الهاتف التلقائي لإطار عمل Android Telecom بإعلام تطبيقك بحالة الاتصال من خلال استخدام مدير الدور والخدمة أثناء الاتصال لإنشاء بديل لتطبيق الهاتف التلقائي على جهاز Android، وتنفيذ واجهة برمجة تطبيقات InCallService. يجب أن تستوفي عملية التنفيذ المتطلبات التالية:

ويجب ألا تتضمّن أي إمكانية للاتصال، ويجب أن تتكون فقط من واجهة مستخدم للاتصال. ويجب أن يتعامل مع جميع المكالمات التي يُدرك إطار عمل الاتصالات بها، ولا يضع افتراضات حول طبيعة المكالمات. على سبيل المثال، يجب ألّا يتم افتراض أنّ المكالمات الهاتفية هي مكالمات هاتفية مستنِدة إلى شريحة SIM، كما يجب عدم تطبيق قيود على المكالمات تستند إلى أيٍ من ConnectionService، مثل فرض قيود الاتصال الهاتفي لمكالمات الفيديو.

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


مثال على تطبيق للاتصال مثال على تطبيق للاتصال يستخدم واجهة المستخدم الخاصة به

يتضمّن إطار عمل Android حزمة android.telecom التي تتيح يحتوي على فئات تساعدك في إنشاء تطبيق للاتصالات وفقًا للاتصالات إطار العمل. يوفّر إنشاء تطبيقك وفقًا لإطار عمل الاتصالات الفوائد التالية:

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

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

في بيان التطبيق، يجب التصريح بأنّ تطبيقك يستخدم MANAGE_OWN_CALLS إذن، كما هو موضَّح في المثال التالي:

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

لمزيد من المعلومات حول الإعلان عن أذونات التطبيق، يُرجى الاطّلاع على الأذونات:

يجب الإفصاح عن خدمة تحدّد الفئة التي تنفِّذ صف واحد (ConnectionService) في تطبيقك. الاتصالات أن تقدّم الخدمة بيانًا عن إذن BIND_TELECOM_CONNECTION_SERVICE قادرًا على الالتزام بها. يوضّح المثال التالي كيفية الإعلان عن الخدمة في بيان التطبيق:

<service android:name="com.example.MyConnectionService"
    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.ConnectionService" />
    </intent-filter>
</service>

لمزيد من المعلومات حول الإفصاح عن مكوّنات التطبيق، بما في ذلك الخدمات، يُرجى الاطّلاع على مكوّنات التطبيق:

تنفيذ خدمة الاتصال

يجب أن يوفّر تطبيق الاتصال فئة ConnectionService التي يمكن أن يرتبط بها النظام الفرعي للاتصالات. يجب أن يتم إلغاء تنفيذ ConnectionService الطرق التالية:

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

يستدعي النظام الفرعي للاتصالات هذه الطريقة استجابةً تطبيقك يتصل بخدمة placeCall(Uri, Bundle) لإنشاء مكالمة صادرة جديدة يعرض تطبيقك مثالاً جديدًا لتنفيذ فئة Connection (لمزيد من المعلومات، يُرجى الاطّلاع على تنفيذ الربط) لتمثيل التجربة الجديدة مكالمة صادرة. يمكنك زيادة تخصيص الاتصال الصادر من خلال تنفيذ الإجراءات التالية:

onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

يطلب النظام الفرعي للاتصالات هذه الطريقة عندما يستدعي تطبيقك طريقة placeCall(Uri, Bundle) ولا يمكن لهذه المكالمة الصادرة. النهج التحليلي. واستجابةً لهذا الموقف، يجب أن يخبر التطبيق المستخدم (بخصوص مثلًا، استخدام مربع تنبيه أو إشعار منبثق) بأنه يتعذر إرسال المكالمة الصادرة . قد لا يتمكّن تطبيقك من إجراء مكالمة إذا كانت هناك مشكلة جارية. أو إذا كانت هناك مكالمة جارية في تطبيق آخر لا يمكن الانتظار قبل إجراء مكالمتك.

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

يطلب النظام الفرعي للاتصالات هذه الطريقة عندما يستدعي تطبيقك طريقة addNewIncomingCall(PhoneAccountHandle, Bundle). لإبلاغ النظام بمكالمة واردة جديدة في التطبيق. يعرض تطبيقك مثال جديد لعملية تنفيذ Connection (على سبيل المثال للحصول على مزيد من المعلومات، يمكنك الاطّلاع على مقالة تنفيذ الربط) لتمثيل المكالمة الواردة الجديدة. يمكنك زيادة تخصيص الإشعارات الواردة الاتصال عن طريق تنفيذ الإجراءات التالية:

onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

يطلب النظام الفرعي للاتصالات هذه الطريقة عندما يطلب تطبيقك طريقة addNewIncomingCall(PhoneAccountHandle, Bundle) لإعلام شركة الاتصالات مكالمة واردة جديدة، ولكن لا يُسمح بالمكالمة الواردة (للمزيد من المعلومات، راجع قيود الاتصال). يجب أن رفض المكالمة الواردة تلقائيًا، ويمكنك نشر إشعار اختياريًا مستخدم المكالمة الفائتة.

تنفيذ عملية الربط

يجب أن ينشئ تطبيقك فئة فرعية من Connection من أجل لتمثيل المكالمات في تطبيقك. يجب عليك إلغاء الطرق التالية في عملية التنفيذ:

onShowIncomingCallUi()

يستدعي النظام الفرعي للاتصالات هذه الطريقة عند إضافة مكالمة واردة جديدة يجب أن يعرض التطبيق واجهة المستخدم للمكالمات الواردة

onCallAudioStateChanged(CallAudioState)

يستدعي النظام الفرعي للاتصالات هذه الطريقة لإعلام التطبيق بأنّ الصوت الحالي تغيير المسار أو الوضع. ويتم استدعاء ذلك استجابةً لتغيير تطبيقك وضع الصوت باستخدام setAudioRoute(int) . يمكن أيضًا استخدام هذه الطريقة في حال غيّر النظام مسار الصوت. (على سبيل المثال، عند فصل سماعة رأس بلوتوث).

onHold()

يستدعي النظام الفرعي للاتصالات هذه الطريقة عندما يريد تعليق المكالمة. استجابةً لهذا الطلب، يجب أن يعلّق تطبيقك المكالمة ثم يستدعي طريقة واحدة (setOnHold()) لإبلاغ النظام إجراء المكالمة. وقد يستدعي النظام الفرعي للاتصالات هذه الطريقة في حال خدمة أثناء المكالمة، مثل Android Auto، والتي تعرض مكالمتك تريد ترحيل طلب مستخدم لتعليق المكالمة. كما يستدعي النظام الفرعي للاتصالات هذه الطريقة إذا أجرى المستخدم مكالمة نشطة في تطبيق آخر. لمزيد من المعلومات، معلومات عن الخدمات الواردة أثناء الاتصال، يُرجى الاطّلاع على InCallService.

onUnhold()

ويستدعي النظام الفرعي للاتصالات هذه الطريقة عندما يريد استئناف مكالمة معلّقة. بعد استئناف تطبيقك المكالمة، من المفترض أن يستدعي setActive() لإبلاغ النظام بأن المكالمة لم تعد معلَّقة. الاتصالات هذه الطريقة عند طلب خدمة أثناء الاتصال، مثل Android Auto، الذي يشير إلى مكالمتك تريد إرسال طلب لاستئناف المكالمة. بالنسبة مزيد من المعلومات عن الخدمات الواردة في المكالمة، يُرجى الاطّلاع على InCallService.

onAnswer()

يستدعي النظام الفرعي للاتصالات هذه الطريقة لإبلاغ لتطبيقك بأن يتم الرد على أي مكالمة واردة. بعد ردّ التطبيق المكالمة، من المفترض أن يستدعي setActive() لإبلاغ النظام بأنه تم الرد على المكالمة. الاتصالات هذه الطريقة عندما يضيف تطبيقك مكالمة واردة جديدة هناك مكالمة جارية حاليًا في تطبيق آخر لا يمكن تعليقها. يعرض النظام الفرعي للاتصالات واجهة مستخدم المكالمات الواردة نيابةً عن تطبيقك في هذه الحالات. يوفر إطار العمل طريقة مع تحميل زائد توفر لتحديد حالة الفيديو التي يتم الرد بها على المكالمة. لمزيد من المعلومات، المعلومات، يمكنك الاطّلاع على onAnswer(int).

onReject()

يطلب النظام الفرعي للاتصالات هذه الطريقة عندما يريد رفض رسالة الاتصال. بعد رفض تطبيقك للمكالمة، من المفترض أن يطلب setDisconnected(DisconnectCause) مع تحديد REJECTED كمعلَمة. يجب أن ثم استدعاء طريقة destroy() لإعلام النظام الذي عالج فيه التطبيق المكالمة. اتصالات النظام الفرعي للاتصالات لهذه الطريقة عندما يرفض المستخدم مكالمة واردة من تطبيقك.

onDisconnect()

يطلب النظام الفرعي للاتصالات هذه الطريقة عندما يريد قطع مكالمة. بعد انتهاء المكالمة، من المفترض أن يستدعي تطبيقك طريقة setDisconnected(DisconnectCause) ويحدّد LOCAL كمَعلمة للإشارة إلى أنّ تسبّب طلب المستخدم في قطع الاتصال. من المفترض أن يستدعي التطبيق بعد ذلك طريقة destroy() لإعلام شركة الاتصالات النظام الفرعي الذي عالج فيه التطبيق المكالمة. قد يستدعي النظام هذه الطريقة عندما يقطع المستخدم مكالمة من خلال خدمة أخرى أثناء الاتصال، مثل Android Auto: يستدعي النظام أيضًا هذه الطريقة عندما يجب أن يكون اتصالك غير متصل للسماح بإجراء مكالمة أخرى، على سبيل المثال، إذا أراد المستخدم لإجراء مكالمة طوارئ. لمزيد من المعلومات عن الخدمات الواردة أثناء الاتصال، يُرجى الاطّلاع على InCallService

التعامل مع سيناريوهات الاتصال الشائعة

الاستفادة من واجهة برمجة التطبيقات ConnectionService في مكالمتك التفاعل مع الفئات الأخرى في android.telecom طرد. توضّح الأقسام التالية سيناريوهات الاتصال الشائعة وكيفية التطبيقات استخدام واجهات برمجة التطبيقات لمعالجتها.

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

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

ما مِن مكالمات نشطة في التطبيقات الأخرى

للردّ على المكالمات الواردة عندما لا تكون هناك مكالمات نشطة في التطبيقات الأخرى، اتّبِع الخطوات التالية: الخطوات التالية:

  1. يتلقّى تطبيقك مكالمة واردة جديدة باستخدام الآليات المعتادة.
  2. استخدِم طريقة addNewIncomingCall(PhoneAccountHandle, Bundle) من أجل لإعلام النظام الفرعي للاتصالات بشأن المكالمة الواردة الجديدة.
  3. يرتبط النظام الفرعي للاتصالات بتنفيذ ConnectionService في تطبيقك ويطلب نسخة افتراضية جديدة. من الفئة Connection التي تمثل البيانات الواردة الجديدة باستخدام طريقة onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. يُعلِم النظام الفرعي للاتصالات تطبيقك بأنّه يجب أن يعرض مكالمته الواردة. من واجهة المستخدم باستخدام طريقة onShowIncomingCallUi().
  5. يعرض تطبيقك واجهة المستخدم الواردة باستخدام إشعار مرتبط الإذن بالعرض ملء الشاشة. لمزيد من المعلومات، يُرجى الاطّلاع على "onShowIncomingCallUi()".
  6. ويجب استدعاء الطريقة setActive() إذا كان المستخدم المكالمة الواردة، أو setDisconnected(DisconnectCause) مع تحديد REJECTED على أنها المعلمة متبوعة إلى طريقة destroy() إذا كان المستخدم يرفض المكالمة الواردة.

المكالمات النشطة في التطبيقات الأخرى التي لا يمكن تعليقها

للرد على المكالمات الواردة عندما تكون هناك مكالمات نشطة في تطبيقات أخرى لا يمكنها قيد الانتظار، يُرجى اتّباع الخطوات التالية:

  1. يتلقّى تطبيقك مكالمة واردة جديدة باستخدام الآليات المعتادة.
  2. استخدِم طريقة addNewIncomingCall(PhoneAccountHandle, Bundle) من أجل لإعلام النظام الفرعي للاتصالات بشأن المكالمة الواردة الجديدة.
  3. يرتبط النظام الفرعي للاتصالات بتنفيذ ConnectionService في تطبيقك ويطلب نسخة افتراضية جديدة. للكائن Connection الذي يمثل العنصر الجديد مكالمة واردة باستخدام طريقة onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. يعرض النظام الفرعي للاتصالات واجهة مستخدم المكالمات الواردة لمكالمتك الواردة.
  5. إذا قبل المستخدم المكالمة، يطلب النظام الفرعي للاتصالات باستخدام طريقة onAnswer(). يجب استدعاء طريقة "setActive()" للإشارة إلى شركة الاتصالات. النظام الفرعي الذي يتصل فيه الاتصال الآن.
  6. إذا رفض المستخدم المكالمة، يطلب النظام الفرعي للاتصالات باستخدام طريقة onReject(). يجب استدعاء طريقة setDisconnected(DisconnectCause) مع تحديد REJECTED كمعلمة متبوعة لاستدعاء طريقة destroy().

إجراء مكالمات صادرة

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

لإجراء مكالمة صادرة، اتبع الخطوات التالية:

  1. يبدأ المستخدم مكالمة صادرة داخل تطبيقك.
  2. استخدِم الطريقة placeCall(Uri, Bundle) لإبلاغ النظام الفرعي للاتصالات حول المكالمات الصادرة الجديدة. اتّبِع الخطوات التالية اعتبارات معلمات الطريقة:
    • تمثل المعلمة Uri العنوان الذي يمكن فيه الذي يتم إجراء اتصاله إليه. بالنسبة إلى أرقام الهواتف العادية، استخدِم معرّف الموارد المنتظم (URI) tel:. .
    • تتيح لك مَعلمة Bundle تقديم المعلومات. حول تطبيق الاتصال من خلال إضافة العنصر PhoneAccountHandle في تطبيقك إلى العنصر الإضافي EXTRA_PHONE_ACCOUNT_HANDLE. يجب أن يوفّر التطبيق الكائن PhoneAccountHandle في كل مكالمة صادرة.
    • وتتيح لك مَعلمة Bundle أيضًا تحديد ما إذا كانت تشتمل المكالمة الصادرة على فيديو من خلال تحديد قيمة STATE_BIDIRECTIONAL في السمة الإضافية EXTRA_START_CALL_WITH_VIDEO_STATE. ضع في الاعتبار أنه بشكل افتراضي، يوجه النظام الفرعي للاتصالات مكالمات الفيديو إلى مكبّر الصوت.
  3. يرتبط النظام الفرعي للاتصالات بـ ConnectionService في تطبيقك. التنفيذ.
  4. إذا تعذّر على تطبيقك إجراء مكالمة صادرة، يتم إجراء مكالمات النظام الفرعي للاتصالات طريقة onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) إبلاغ تطبيقك بتعذُّر إجراء المكالمة في الوقت الحالي. تطبيقك إبلاغ المستخدم بتعذُّر إجراء المكالمة.
  5. إذا كان تطبيقك قادرًا على إجراء مكالمة صادرة، فإن النظام الفرعي للاتصالات onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest) . من المفترض أن يعرض تطبيقك نسخة افتراضية من فئة Connection لتمثيل المكالمة الصادرة الجديدة. بالنسبة مزيد من المعلومات عن الخصائص التي يجب إعدادها في عملية الربط راجِع تنفيذ خدمة الاتصال.
  6. عندما يتم إجراء المكالمة الصادرة، يُرجى الاتصال بطريقة "setActive()" لإبلاغ النظام الفرعي للاتصالات. أن المكالمة نشطة.

إنهاء مكالمة

لإنهاء مكالمة، اتبع الخطوات التالية:

  1. يجب استدعاء الدالة setDisconnected(DisconnectCause) التي ترسل LOCAL كمَعلمة إذا كان المستخدم. أنهى المكالمة، أو إرسال REMOTE كمعلمة إذا أنهى الطرف الآخر الاتصال.
  2. وعليك استدعاء الطريقة destroy().

قيود المكالمات

لضمان تجربة اتصال متسقة وبسيطة للمستخدمين، يجب أن تتواصل بعض القيود لإدارة المكالمات على الجهاز. بالنسبة ضع في اعتبارك أن المستخدم قد ثبَّت تطبيقين للاتصال يقدمان واجهة برمجة التطبيقات ConnectionService المُدارة ذاتيًا وFooTalk BarTalk. في هذه الحالة، تنطبق القيود التالية:

  • بالنسبة إلى الأجهزة التي تعمل بالإصدار 27 من واجهة برمجة التطبيقات أو المستويات الأدنى، يمكن لتطبيق واحد فقط الاحتفاظ مكالمة جارية في أي وقت محدد. يعني هذا القيد أنه في حين أن المستخدم لديه مكالمة جارية باستخدام تطبيق FooTalk، لا يمكن لتطبيق BarTalk بدء أو استقبال مكالمة جديدة.

    على الأجهزة التي تعمل بالمستوى 28 من واجهة برمجة التطبيقات أو المستويات الأعلى، في حال استخدام كل من FooTalk وBarTalk بيان CAPABILITY_SUPPORT_HOLD أو CAPABILITY_HOLD ثم يمكن للمستخدم الاحتفاظ بأكثر من مكالمة جارية عن طريق التبديل بين التطبيقات لبدء مكالمة أخرى أو الرد عليها.

  • إذا كان المستخدم يشارك في مكالمات مُدارة عادية (على سبيل المثال، باستخدام تطبيق الهاتف أو برنامج الاتصال المدمج)، لا يمكن أن يكون المستخدم في المكالمات التي نشأت من تطبيقات الاتصال وهذا يعني أنه إذا كان المستخدم يجري مكالمة عادية باستخدام مشغّل شبكة الجوّال، فلا يمكن أن يكونا أيضًا في مكالمة FooTalk أو BarTalk بشكل متزامن.

  • ينقطع النظام الفرعي للاتصالات مكالمات تطبيقك إذا طلب المستخدم مكالمة طوارئ.

  • لا يمكن لتطبيقك استقبال المكالمات أو إجراؤها أثناء إجراء المستخدم لمكالمة طوارئ.

  • إذا كانت هناك مكالمة جارية في تطبيق آخر للاتصال عند تلقّي تطبيقك مكالمة واردة، فإن الرد على المكالمة الواردة يؤدي إلى إنهاء أي مكالمات جارية في تطبيق آخر. يجب ألا يعرض التطبيق واجهة المستخدم المعتادة للمكالمات الواردة. يعرض إطار عمل الاتصالات واجهة مستخدم المكالمات الواردة ويبلغك إنهاء مكالماته الجارية عند ردّ المستخدم على المكالمة الجديدة هذا النمط يعني أنه إذا كان المستخدم في مكالمة FooTalk ويتلقى تطبيق BarTalk مكالمة واردة، يُعلم إطار عمل الاتصالات المستخدم بأن لديه مكالمة BarTalk الواردة وأن الرد على مكالمة BarTalk سوف ينتهي مكالمة FooTalk.

استخدام تطبيق الهاتف التلقائي

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

يوفّر تطبيق الهاتف التلقائي واجهة مستخدم أثناء إجراء مكالمة، ويتم تشغيل ليس في وضع السيارة (أي أن UiModeManager#getCurrentModeType() ليس في وضع السيارة Configuration.UI_MODE_TYPE_CAR).

لملء الدور "RoleManager.ROLE_DIALER"، يجب أن يستوفي التطبيق عدد المتطلبات:

  • ويجب أن يتعامل مع هدف Intent#ACTION_DIAL. وهذا يعني أنّ التطبيق يجب أن يوفّر واجهة مستخدم لوحة اتصال يمكن للمستخدم من خلالها بدء مكالمات صادرة.
  • يجب أن تنفيذ واجهة برمجة التطبيقات InCallService بالكامل وأن يوفّر مكالمة واردة واجهة مستخدم، بالإضافة إلى واجهة مستخدم للمكالمات الجارية.

ملاحظة: إذا كان التطبيق الذي يملأ RoleManager.ROLE_DIALER يعرض null InCallService أثناء الربط، سينخفض إطار عمل الاتصالات تلقائيًا. استخدام تطبيق "برنامج الاتصال" المُحمَّل مُسبَقًا على الجهاز. سيعرض النظام إشعارًا المستخدم لإعلامه بأن مكالمته استمرت باستخدام تطبيق برنامج الاتصال المُحمَّل مُسبَقًا. يجب ألا يعرض التطبيق مطلقًا ربط null، فإن القيام بذلك يعني أنه لا يفي متطلبات RoleManager.ROLE_DIALER.

ملاحظة: إذا كان تطبيقك يملأ RoleManager.ROLE_DIALER ويُجري تغييرات على عدم تلبية متطلبات هذا الدور بعد الآن، سيُزيل RoleManager تطبيقك تلقائيًا من الدور ويغلقه. تطبيقك. على سبيل المثال، إذا كنت تستخدم من PackageManager.setComponentEnabledSetting(ComponentName, int, int) إلى إيقاف InCallService الذي يفصح عنه تطبيقك آليًا في ملف البيان الخاص به: تطبيقك الشركة بعد الآن في تلبية المتطلبات المتوقعة من RoleManager.ROLE_DIALER

سيتم استخدام برنامج الاتصال المُحمَّل مُسبَقًا دائمًا عندما يُجري المستخدم مكالمة طوارئ، حتى إذا كان يملأ التطبيق الدور "RoleManager.ROLE_DIALER". لضمان النموذج الأمثل المستخدم عند إجراء مكالمة طوارئ، على برنامج الاتصال التلقائي "دائمًا" TelecomManager.placeCall(Uri, Bundle) لإجراء المكالمات (بما في ذلك مكالمات الطوارئ). يضمن هذا الإجراء أنّ المنصة قادرة على التحقق من أنّ الطلب وارد من برنامج الاتصال الافتراضي. إذا كان تطبيق برنامج اتصال غير محمَّل مسبقًا يستخدم Intent#ACTION_CALL لوضع مكالمة طوارئ، ستتم زيادتها إلى تطبيق برنامج الاتصال المُحمَّل مُسبَقًا باستخدام Intent#ACTION_DIAL. للتأكيد؛ فهذه تجربة مستخدم دون المستوى الأمثل.

في ما يلي مثال على تسجيل البيان لنطاق InCallService. البيانات الوصفية تشير السمة TelecomManager#METADATA_IN_CALL_SERVICE_UI إلى أنّ هذا الخيار يهدف تنفيذ InCallService إلى استبدال واجهة المستخدم المضمَّنة أثناء المكالمة. تشير البيانات الوصفية TelecomManager#METADATA_IN_CALL_SERVICE_RINGING إلى أن سيشغِّل تطبيق "InCallService" نغمة الرنين للمكالمات الواردة. عرض أدناه لمزيد من المعلومات حول عرض المكالمة الواردة واجهة المستخدم وتشغيل نغمة الرنين في التطبيق.

 <service android:name="your.package.YourInCallServiceImplementation"
          android:permission="android.permission.BIND_INCALL_SERVICE"
          android:exported="true">
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
          android:value="true" />
      <intent-filter>
          <action android:name="android.telecom.InCallService"/>
      </intent-filter>
 </service>

ملاحظة: يجب عدم وضع علامة على InCallService باستخدام السمة. android:exported="false"؛ قد يؤدي ذلك إلى عدم الالتزام بعملية التنفيذ أثناء المكالمات

بالإضافة إلى تنفيذ واجهة برمجة تطبيقات InCallService، عليك أيضًا الإفصاح عن نشاط في البيان الذي يعالج هدف Intent#ACTION_DIAL يوضح المثال أدناه كيفية القيام بذلك:

 <activity android:name="your.package.YourDialerActivity"
           android:label="@string/yourDialerActivityLabel">
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
           <data android:scheme="tel" />
      </intent-filter>
 </activity>

عندما يثبّت أحد المستخدمين تطبيقك ويشغِّله لأول مرة، يجب عليك استخدام RoleManager ليطلب من المستخدم معرفة ما إذا كان يريد من تطبيقك ليكون تطبيق الهاتف الافتراضي الجديد.

يوضّح الرمز أدناه كيف يمكن لتطبيقك أن يطلب أن يصبح تطبيق الهاتف أو برنامج الاتصال التلقائي:

 private static final int REQUEST_ID = 1;

 public void requestRole() {
     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
     Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
     startActivityForResult(intent, REQUEST_ID);
 }

 public void onActivityResult(int requestCode, int resultCode, Intent data) {
     if (requestCode == REQUEST_ID) {
         if (resultCode == android.app.Activity.RESULT_OK) {
             // Your app is now the default dialer app
         } else {
             // Your app is not the default dialer app
         }
     }
 }

الوصول إلى InCallService في الأجهزة القابلة للارتداء

    إذا كان تطبيقك مصاحبًا تابعًا لجهة خارجية ويريد الوصول إلى واجهات برمجة تطبيقات InCallService، ما يمكن أن تفعله هذه التطبيقات هي:

    1. بيان إذن MANAGE_ONGOING_CALLS في ملف البيان
    2. الإقران بجهاز فعلي قابل للارتداء عبر CompanionDeviceManager API كتطبيق مصاحب. يمكنك الاطّلاع على: https://developer.android.com/guide/topics/connectivity/companion-device-pairing
    3. تنفيذ InCallService هذا باستخدام إذن BIND_INCALL_SERVICE

عرض إشعار المكالمات الواردة

عندما يتلقّى تطبيقك مكالمة واردة جديدة عبر InCallService#onCallAdded(Call)، المسئول عن عرض واجهة المستخدم الخاصة بالمكالمات الواردة. يجب أن يقوم بذلك باستخدام NotificationManager واجهات برمجة تطبيقات لنشر إشعار بمكالمات واردة جديدة

في الحالات التي يفصح فيها تطبيقك عن البيانات الوصفية TelecomManager#METADATA_IN_CALL_SERVICE_RINGING، سيكون مسؤولاً عن تشغيل نغمة الرنين للمكالمات الواردة. يجب أن ينشئ تطبيقك NotificationChannel التي تحدّد نغمة الرنين المطلوبة مثلاً:

 NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
          NotificationManager.IMPORTANCE_MAX);
 // other channel setup stuff goes here.

 // We'll use the default system ringtone for our incoming call notification channel.  You can
 // use your own audio resource here.
 Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
 channel.setSound(ringtoneUri, new AudioAttributes.Builder()
          // Setting the AudioAttributes is important as it identifies the purpose of your
          // notification sound.
          .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
      .build());

 NotificationManager mgr = getSystemService(NotificationManager.class);
 mgr.createNotificationChannel(channel);

عندما يتلقّى تطبيقك مكالمة واردة جديدة، يُنشئ Notification مكالمة واردة وربطها بقناة إشعارات المكالمات الواردة يمكنك تحديد PendingIntent في الإشعار الذي سيؤدي إلى تشغيل وضع ملء الشاشة واجهة المستخدم للمكالمات الواردة وسيعرض إطار عمل "إدارة الإشعارات" إشعارك على شكل إذا كان المستخدم يستخدم الهاتف بشكل نشط. عندما لا يستخدم المستخدم يتم استخدام واجهة مستخدم المكالمات الواردة بملء الشاشة بدلاً من ذلك. مثلاً:

 // Create an intent which triggers your fullscreen incoming call user interface.
 Intent intent = new Intent(Intent.ACTION_MAIN, null);
 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
 intent.setClass(context, YourIncomingCallActivity.class);
 PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
 // Build the notification as an ongoing high priority item; this ensures it will show as
 // a heads up notification which slides down over top of the current content.
 final Notification.Builder builder = new Notification.Builder(context);
 builder.setOngoing(true);
 builder.setPriority(Notification.PRIORITY_HIGH);
 // Set notification content intent to take user to the fullscreen UI if user taps on the
 // notification body.
 builder.setContentIntent(pendingIntent);
 // Set full screen intent to trigger display of the fullscreen UI when the notification
 // manager deems it appropriate.
 builder.setFullScreenIntent(pendingIntent, true);
 // Setup notification content.
 builder.setSmallIcon( yourIconResourceId );
 builder.setContentTitle("Your notification title");
 builder.setContentText("Your notification content.");
 // Use builder.addAction(..) to add buttons to answer or reject the call.
 NotificationManager notificationManager = mContext.getSystemService(
     NotificationManager.class);
 notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
```