يتيح تطبيق الهاتف التلقائي لإطار عمل اتصالات Android إبلاغ تطبيقك بحالة الاتصال عن طريق استخدام مدير الأدوار والخدمة أثناء الاتصال لإنشاء بديل لتطبيق الهاتف التلقائي على جهاز Android، ويمكنك تنفيذ واجهة برمجة التطبيقات InCallService API. يجب أن تستوفي عملية التنفيذ المتطلبات التالية:
يجب ألا يتضمن التطبيق أي إمكانية اتصال، ويجب أن يتألف فقط من واجهة مستخدم للاتصال. يجب أن تتعامل الشركة مع جميع المكالمات التي يدركها إطار عمل الاتصالات، وألا تضع افتراضات حول طبيعة المكالمات. على سبيل المثال، يجب ألا تفترض الميزة أنّ المكالمات الهاتفية مستندة إلى شريحة 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
(لمزيد من المعلومات، يُرجى الاطّلاع على تنفيذ عملية الربط) لتمثيل المكالمة الصادرة الجديدة. يمكنك تخصيص الاتصال الصادر بشكل أكبر عن طريق تنفيذ الإجراءات التالية:- يجب أن يستدعي تطبيقك الطريقة
setConnectionProperties(int)
باستخدام الثابتPROPERTY_SELF_MANAGED
كوسيطة للإشارة إلى أنّ الاتصال نشأ من تطبيق اتصال. - إذا كان تطبيقك يتيح تعليق المكالمات، يمكنك استدعاء الإجراء
setConnectionCapabilities(int)
وضبط الوسيطة على قيمة قناع البت للثابتَينCAPABILITY_HOLD
وCAPABILITY_SUPPORT_HOLD
. - لضبط اسم المتصل، استخدِم طريقة
setCallerDisplayName(String, int)
التي تتجاوز القيمة الثابتةPRESENTATION_ALLOWED
كمَعلمةint
للإشارة إلى أنّه يجب عرض اسم المتصل. - للتأكّد من توفّر حالة الفيديو المناسبة في المكالمة الصادرة، عليك استدعاء طريقة
setVideoState(int)
للكائنConnection
وإرسال القيمة التي تعرضها طريقةgetVideoState()
للعنصرConnectionRequest
.
- يجب أن يستدعي تطبيقك الطريقة
onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)
يستدعي النظام الفرعي للاتصالات هذه الطريقة عندما يستدعي تطبيقك طريقة
placeCall(Uri, Bundle)
ولا يمكن إجراء مكالمة صادرة. استجابةً لهذا الموقف، يجب أن يُعلِم تطبيقك المستخدم (على سبيل المثال، باستخدام مربع تنبيه أو إشعار نخبي) بتعذُّر إجراء المكالمة الصادرة. قد لا يتمكّن تطبيقك من إجراء مكالمة في حال كانت هناك مكالمة طوارئ جارية أو إذا كانت هناك مكالمة جارية في تطبيق آخر لا يمكن تعليقها قبل إجراء مكالمتك.onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
يستدعي النظام الفرعي للاتصالات هذه الطريقة عندما يستدعي تطبيقك طريقة
addNewIncomingCall(PhoneAccountHandle, Bundle)
لإعلام النظام بمكالمة واردة جديدة في تطبيقك. يعرض تطبيقك مثيلاً جديدًا من تنفيذConnection
(للحصول على مزيد من المعلومات، راجِع تنفيذ عملية الربط) لتمثيل المكالمة الواردة الجديدة. يمكنك تخصيص الاتصال الوارد بشكل أكبر عن طريق تنفيذ الإجراءات التالية:- يجب أن يستدعي تطبيقك الطريقة
setConnectionProperties(int)
باستخدام الثابتPROPERTY_SELF_MANAGED
كوسيطة للإشارة إلى أنّ الاتصال نشأ من تطبيق اتصال. - إذا كان تطبيقك يتيح تعليق المكالمات، يمكنك استدعاء الإجراء
setConnectionCapabilities(int)
وضبط الوسيطة على قيمة قناع البت للثابتَينCAPABILITY_HOLD
وCAPABILITY_SUPPORT_HOLD
. - لضبط اسم المتصل، استخدِم طريقة
setCallerDisplayName(String, int)
التي تتجاوز القيمة الثابتةPRESENTATION_ALLOWED
كمَعلمةint
للإشارة إلى أنّه يجب عرض اسم المتصل. - لتحديد رقم الهاتف أو عنوان المكالمة الواردة، استخدِم طريقة
setAddress(Uri, int)
في العنصرConnection
. - للتأكّد من توفّر حالة الفيديو المناسبة في المكالمة الصادرة، عليك استدعاء طريقة
setVideoState(int)
للكائنConnection
وإرسال القيمة التي تعرضها طريقةgetVideoState()
للعنصرConnectionRequest
.
- يجب أن يستدعي تطبيقك الطريقة
onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)
يستدعي النظام الفرعي للاتصالات بهذه الطريقة عندما يستدعي تطبيقك طريقة
addNewIncomingCall(PhoneAccountHandle, Bundle)
لإبلاغ Telecom بمكالمة واردة جديدة، ولكن لا يُسمح بالمكالمة الواردة (لمزيد من المعلومات، يُرجى الاطّلاع على قيود الاتصال). يجب أن يرفض تطبيقك المكالمة الواردة بدون تنبيه، وأن ينشر إشعارًا اختياريًا لإعلام المستخدم بالمكالمة الفائتة.
تنفيذ عملية الربط
يجب أن يُنشئ تطبيقك فئة فرعية من 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
API في مسار الاتصال يستلزم التفاعل مع الفئات الأخرى في حزمة android.telecom
. تصف الأقسام التالية سيناريوهات الاتصال الشائعة والكيفية التي يجب أن يستخدم بها
التطبيق واجهات برمجة التطبيقات للتعامل معها.
الرد على المكالمات الواردة
يتغير تدفق التعامل مع المكالمات الواردة ما إذا كانت هناك مكالمات في التطبيقات الأخرى أم لا. ويرجع السبب في الاختلاف في التدفقات إلى أنّه يجب أن يضع إطار عمل الاتصالات بعض القيود عند وجود مكالمات نشطة في التطبيقات الأخرى لضمان بيئة مستقرة لجميع تطبيقات الاتصال على الجهاز. لمزيد من المعلومات، يمكنك الاطّلاع على قيود الاتصال.
ما مِن مكالمات نشطة في التطبيقات الأخرى
للردّ على المكالمات الواردة في حال عدم توفّر مكالمات نشطة في التطبيقات الأخرى، اتّبِع الخطوات التالية:
- يتلقّى تطبيقك مكالمة واردة جديدة باستخدام الآليات المعتادة.
- استخدِم الطريقة
addNewIncomingCall(PhoneAccountHandle, Bundle)
لإعلام النظام الفرعي للاتصالات بالمكالمة الواردة الجديدة. - يرتبط النظام الفرعي للاتصالات بتطبيق
ConnectionService
لتطبيقك ويطلب مثيلاً جديدًا من الفئةConnection
التي تمثّل المكالمة الواردة الجديدة باستخدام طريقةonCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
. - يُعلِم النظام الفرعي للاتصالات تطبيقك بضرورة عرض واجهة المستخدم للمكالمات الواردة باستخدام طريقة
onShowIncomingCallUi()
. - يعرض تطبيقك واجهة المستخدم الواردة باستخدام إشعار مرتبط بملء الشاشة. لمزيد من المعلومات، يُرجى الاطّلاع على "
onShowIncomingCallUi()
". - يمكنك استدعاء الإجراء
setActive()
إذا قبل المستخدم المكالمة الواردة، أوsetDisconnected(DisconnectCause)
مع تحديدREJECTED
لتكون المعلَمة، متبوعة باستدعاء لطريقةdestroy()
إذا رفض المستخدم المكالمة الواردة.
المكالمات النشطة في التطبيقات الأخرى والتي لا يمكن تجميدها
للرد على المكالمات الواردة في حال وجود مكالمات نشطة في التطبيقات الأخرى ولا يمكن تعليقها، اتّبع الخطوات التالية:
- يتلقّى تطبيقك مكالمة واردة جديدة باستخدام الآليات المعتادة.
- استخدِم الطريقة
addNewIncomingCall(PhoneAccountHandle, Bundle)
لإعلام النظام الفرعي للاتصالات بالمكالمة الواردة الجديدة. - يرتبط النظام الفرعي للاتصالات بتطبيق
ConnectionService
في تطبيقك ويطلب مثيلاً جديدًا من عنصرConnection
الذي يمثّل المكالمة الواردة الجديدة باستخدام طريقةonCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
. - يعرض النظام الفرعي للاتصالات واجهة المستخدم للمكالمات الواردة.
- في حال قبول المستخدم المكالمة، يتصل النظام الفرعي للاتصالات بالطريقة
onAnswer()
. يجب الاتصال بطريقةsetActive()
للإشارة إلى النظام الفرعي للاتصالات إلى أنّ المكالمة متصلة الآن. - إذا رفض المستخدم المكالمة، يستدعي النظام الفرعي للاتصالات طريقة
onReject()
. عليك استدعاء الإجراءsetDisconnected(DisconnectCause)
مع تحديدREJECTED
كمَعلمة متبوعة باستدعاء الطريقةdestroy()
.
إجراء مكالمات صادرة
يتضمن تدفق إجراء مكالمة صادرة التعامل مع احتمال عدم إمكانية إجراء المكالمة بسبب القيود التي يفرضها إطار عمل الاتصالات. لمزيد من المعلومات، يُرجى الاطّلاع على قيود الاتصال.
لإجراء مكالمة صادرة، اتّبع الخطوات التالية:
- يبدأ المستخدم مكالمة صادرة داخل تطبيقك.
- استخدِم طريقة
placeCall(Uri, Bundle)
لإعلام النظام الفرعي للاتصالات بالمكالمة الصادرة الجديدة. يُرجى مراعاة الاعتبارات التالية لمَعلمات الطريقة:- تمثّل المَعلمة
Uri
العنوان الذي يتم إجراء الطلب عليه. بالنسبة إلى أرقام الهواتف العادية، استخدِم نظام معرِّف الموارد المنتظم (URI)tel:
. - تسمح لك المعلَمة
Bundle
بتوفير معلومات عن تطبيق الاتصال من خلال إضافة الكائنPhoneAccountHandle
في تطبيقك إلى عنصرEXTRA_PHONE_ACCOUNT_HANDLE
الإضافي. يجب أن يوفر تطبيقك العنصرPhoneAccountHandle
لكل مكالمة صادرة. - تسمح لك المعلَمة
Bundle
أيضًا بتحديد ما إذا كانت المكالمة الصادرة تتضمّن فيديو من خلال تحديد قيمةSTATE_BIDIRECTIONAL
فيEXTRA_START_CALL_WITH_VIDEO_STATE
الإضافية. ضع في اعتبارك ذلك تلقائيًا، يوجِّه النظام الفرعي للاتصالات مكالمات الفيديو إلى مكبّر الصوت.
- تمثّل المَعلمة
- يرتبط النظام الفرعي للاتصالات بتطبيق
ConnectionService
في تطبيقك. - إذا لم يتمكّن تطبيقك من إجراء مكالمة صادرة، يطلب النظام الفرعي للاتصالات
من طريقة
onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)
لإعلام تطبيقك بأنّه لا يمكن إجراء الاتصال في الوقت الحالي. يجب أن يُعلم تطبيقك المستخدم بأنه لا يمكن إجراء المكالمة. - إذا كان تطبيقك قادرًا على إجراء المكالمة الصادرة، يُطلق على النظام الفرعي للاتصالات طريقة
onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
. يجب أن يعرض تطبيقك مثيلاً من فئةConnection
لتمثيل المكالمة الصادرة الجديدة. لمزيد من المعلومات حول السمات التي يجب إعدادها في عملية الربط، راجِع تنفيذ خدمة الربط. - عند إجراء المكالمة الصادرة، يمكنك الاتصال بطريقة
setActive()
لإعلام النظام الفرعي للاتصالات بأنّ المكالمة نشطة.
إنهاء مكالمة
لإنهاء مكالمة، اتّبِع الخطوات التالية:
- يمكنك استدعاء
setDisconnected(DisconnectCause)
مع إرسالLOCAL
كمَعلمة إذا أنهى المستخدم المكالمة، أو إرسالREMOTE
باعتبارها مَعلمة إذا أنهى الطرف الآخر الاتصال. - استدعِ الطريقة
destroy()
.
القيود المفروضة على المكالمات
لضمان تجربة اتصال متسقة وبسيطة للمستخدمين، يفرض إطار عمل الاتصالات بعض القيود لإدارة المكالمات على الجهاز. على سبيل المثال، قد يكون المستخدم قد ثبّت تطبيقَين للاتصال يستخدمان واجهة برمجة التطبيقات ConnectionService
المُدارة ذاتيًا، وهما FooTalk وBarTalk. في هذه الحالة، تنطبق القيود التالية:
على الأجهزة التي تعمل بالمستوى 27 من واجهة برمجة التطبيقات أو أقل، يمكن لتطبيق واحد فقط المحافظة على مكالمة جارية في أي وقت. يعني هذا القيد أنّه على الرغم من أنّ المستخدم لديه مكالمة جارية باستخدام تطبيق FooTalk، لا يمكن لتطبيق BarTalk بدء مكالمة جديدة أو تلقّيها.
على الأجهزة التي تعمل من المستوى 28 أو أعلى من واجهة برمجة التطبيقات، إذا أعلن كل من FooTalk وBarTalk عن إذنَي
CAPABILITY_SUPPORT_HOLD
وCAPABILITY_HOLD
، يمكن للمستخدم الاحتفاظ بأكثر من مكالمة جارية واحدة من خلال التبديل بين التطبيقَين لبدء مكالمة أخرى أو الردّ عليها.إذا كان المستخدم مشاركًا في مكالمات مُدارة عادية (على سبيل المثال، باستخدام تطبيق الهاتف أو تطبيق Dialer المُدمَج)، لا يمكن للمستخدم تلقّي المكالمات التي تنشأ من تطبيقات الاتصال. وهذا يعني أنّه إذا كان المستخدم في مكالمة عادية باستخدام مشغّل شبكة الجوّال، لا يمكنه أيضًا المشاركة في مكالمة FooTalk أو BarTalk بشكل متزامن.
يقطع النظام الفرعي للاتصالات مكالمات تطبيقك إذا اتصل المستخدم بمكالمة طوارئ.
لا يمكن لتطبيقك تلقّي مكالمات أو إجرائها عندما يُجري المستخدم مكالمة طوارئ.
إذا كانت هناك مكالمة جارية في تطبيق آخر للاتصال عند تلقّي تطبيقك مكالمة واردة، سيؤدي الرد على المكالمة الواردة إلى إنهاء أي مكالمات جارية في التطبيق الآخر. يجب ألا يعرض التطبيق واجهة المستخدم المعتادة للمكالمات الواردة. يعرض إطار عمل الاتصالات واجهة مستخدم المكالمات الواردة ويُعلِم المستخدم بأنّ الردّ على المكالمة الجديدة سينهي المكالمات الجارية. ويعني هذا أنّه إذا كان المستخدم مشاركًا في مكالمة FooTalk وكان تطبيق BarTalk يتلقى مكالمة واردة، يُعلِم إطار عمل الاتصالات المستخدم بأنّ لديه مكالمة BarTalk قادمة وأن الردّ على مكالمة BarTalk سيؤدي إلى إنهاء مكالمة FuTalk.
اختيار تطبيق "الهاتف" التلقائي
تطبيق برنامج الاتصال أو الهاتف التلقائي هو التطبيق الذي يوفّر واجهة المستخدم أثناء المكالمة
أثناء إجراء مكالمة. ويزوّد المستخدم أيضًا بوسيلة لبدء المكالمات والاطّلاع على سجلّ المكالمات
على جهازه. جهاز مزوَّد بنظام اتصال تلقائي أو تطبيق هاتف تلقائي مزود به النظام. يمكن للمستخدم
اختيار تطبيق واحد لتولي هذا الدور من تطبيق النظام. في حال أراد أحد التطبيقات
أن يؤدي هذا الدور، يستخدم "RoleManager
" ليطلب منه تولّي
الدور "RoleManager.ROLE_DIALER
".
يوفِّر تطبيق الهاتف التلقائي واجهة مستخدم أثناء إجراء مكالمة، ولا يكون الجهاز
في وضع السيارة (أي أنّ UiModeManager#getCurrentModeType()
ليس
Configuration.UI_MODE_TYPE_CAR
).
لشغل دور RoleManager.ROLE_DIALER
، يجب أن يستوفي التطبيق عددًا من المتطلبات:
- يجب أن يتعامل مع الغرض من
Intent#ACTION_DIAL
. ويعني هذا أنّه يجب أن يوفّر التطبيق واجهة المستخدم الخاصة بلوحة الاتصال حتى يتمكن المستخدم من بدء المكالمات الصادرة. - يجب أن ينفِّذ واجهة برمجة التطبيقات
InCallService
API بشكل كامل وأن يوفر واجهة مستخدم للمكالمات الواردة، بالإضافة إلى واجهة مستخدم للمكالمات الجارية.
ملاحظة: إذا عرض التطبيق الذي يملأ RoleManager.ROLE_DIALER
رمز InCallService
null
أثناء الربط، سيعود إطار عمل الاتصالات
تلقائيًا إلى استخدام تطبيق برنامج الاتصال المُحمَّل مسبقًا على الجهاز. سيعرض النظام إشعارًا
للمستخدم لإبلاغه بمواصلة مكالمته باستخدام تطبيق برنامج الاتصال المحمَّل مسبقًا. يجب ألا يعرض تطبيقك ربط 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، يمكن تنفيذ ما يلي:
- تقديم إذن MANAGE_ONGOING_CALLS في ملف البيان
- يمكنك ربط الجهاز بجهاز قابل للارتداء عبر واجهة برمجة تطبيقات
CompanionDeviceManager
كتطبيق مصاحب. يمكنك الاطّلاع على: https://developer.android.com/guide/topics/connectivity/companion-device-pairing - تنفيذ 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());
```