ساختن برنامه تلفن پیش‌فرض

برنامه تلفن پیش‌فرض به چارچوب Android Telecom اجازه می‌دهد بااستفاده از مدیر نقش و سرویس حین تماس، برنامه شما را از وضعیت تماس مطلع کند تا جایگزینی برای برنامه تلفن پیش‌فرض در دستگاه Android ایجاد کند و میانای برنامه‌سازی کاربردی InCallService را پیاده‌سازی کند. پیاده‌سازی شما باید الزامات زیر را برآورده کند:

نباید هیچ‌گونه قابلیت تماس داشته باشد و باید صرفاً شامل میانای کاربر برای تماس باشد. این برنامه باید همه تماس‌هایی را که چارچوب Telecom از آن‌ها مطلع است مدیریت کند و درباره ماهیت تماس‌ها فرضیه‌سازی نکند. برای مثال، نباید فرض کند تماس‌ها از نوع تماس‌های تلفنی مبتنی بر سیم‌کارت هستند، یا محدودیت‌های تماس را که براساس هر 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 از تماس ورودی جدید فراخوانی می‌کند، زیرسیستم 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 است. بخش‌های زیر سناریوهای رایج تماس و نحوه استفاده برنامه‌تان از میاناهای برنامه‌سازی کاربردی برای مدیریت آن‌ها را شرح می‌دهد.

پاسخ‌گویی به تماس‌های ورودی

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

تماس فعالی در برنامه‌های دیگر وجود ندارد

برای پاسخ دادن به تماس‌های ورودی وقتی تماس فعالی در برنامه‌های دیگر وجود ندارد، این مراحل را دنبال کنید:

  1. برنامه شما بااستفاده از سازوکارهای معمول خود، تماس ورودی جدیدی دریافت می‌کند.
  2. از روش addNewIncomingCall(PhoneAccountHandle, Bundle) برای اطلاع‌رسانی به زیرسیستم مخابراتی درباره تماس ورودی جدید استفاده کنید.
  3. زیرسیستم مخابراتی به پیاده‌سازی ConnectionService برنامه شما متصل می‌شود و بااستفاده از روش onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)، نمونه جدیدی از کلاس Connection را که نشان‌دهنده تماس ورودی جدید است درخواست می‌کند.
  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 API خودمدیریت FooTalk و BarTalk را پیاده‌سازی می‌کنند. در این حالت، محدودیت‌های زیر اعمال می‌شود:

  • در دستگاه‌هایی که از سطح میانای برنامه‌سازی کاربردی ۲۷ یا پایین‌تر استفاده می‌کنند، فقط یک برنامه می‌تواند در هر زمان مشخصی تماس درحال انجام را حفظ کند. این محدودیت به این معنی است که وقتی کاربر بااستفاده از برنامه FooTalk درحال برقراری تماس است، برنامه BarTalk نمی‌تواند تماس جدیدی برقرار یا دریافت کند.

    در دستگاه‌هایی که از میانای برنامه کاربردی سطح ۲۸ یا بالاتر استفاده می‌کنند، اگر 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 را برگرداند، چارچوب Telecom به‌طور خودکار به استفاده از برنامه شماره‌گیر پیش‌بارشده در دستگاه برمی‌گردد. سیستم اعلانی به کاربر نشان می‌دهد تا به او اطلاع دهد که تماسش بااستفاده از برنامه شماره‌گیر پیش‌بارشده ادامه یافته است. برنامه شما هرگز نباید اتصال 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 API برای پست کردن اعلان تماس ورودی جدید انجام شود.

وقتی برنامه‌تان فراداده 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());
```