מסגרת Android Telecom (שנקראת גם 'Telecom') מנהלת שיחות אודיו ושיחות וידאו במכשיר עם Android. הנתונים האלה כוללים שיחות שמבוססות על כרטיס SIM, כמו שיחות שמשתמשות במסגרת הטלפוניה, ושיחות VoIP שמטמיעות את ספריית Jetpack Core-Telecom
.
הרכיבים העיקריים שמנוהלים על ידי Telecom הם ConnectionService
ו-InCallService
.
הטמעה של ConnectionService
מבוססת על טכנולוגיות כמו PSTN כדי לחבר שיחות לצדדים אחרים. ההטמעה הנפוצה ביותר של ConnectionService
בטלפון היא ConnectionService
של טלפוניה. הוא מחבר שיחות דרך הספק.
הטמעה של InCallService
מספקת ממשק משתמש לשיחות שמנוהלות על ידי חברת התקשורת, ומאפשרת למשתמש לשלוט בשיחות האלה וליצור איתן אינטראקציה. ההטמעה הנפוצה ביותר של InCallService
היא אפליקציית הטלפון שכלולה בחבילה עם המכשיר.
חברת התקשורת פועלת כמרכז טלפוניה. הוא מפנה את השיחות שהטמעות ConnectionService
מספקות לממשקי המשתמש של מבצעי הקריאה שהטמעות InCallService
מספקות.
כדאי להטמיע את ממשקי ה-API של Telecom מהסיבות הבאות:
- כדי ליצור תחליף לאפליקציית הטלפון של המערכת.
- לשימוש בפתרונות לשיחות בחוויית השיחות ב-Android.
יצירת אפליקציית טלפון חלופית
כדי ליצור תחליף לאפליקציית הטלפון שמוגדרת כברירת מחדל במכשיר Android, צריך להטמיע את ה-API של InCallService
. ההטמעה צריכה לעמוד בדרישות הבאות:
- לא יכולה להיות לה יכולת ביצוע שיחות, והיא צריכה להכיל רק את ממשק המשתמש לשיחות.
- הוא צריך לטפל בכל השיחות שמערכת Telecom מודעת אליהן, ולא להניח הנחות לגבי אופי השיחות. לדוגמה, אסור להניח שהשיחות הן שיחות טלפוניה שמבוססות על כרטיס SIM, או להחיל הגבלות על שיחות שמבוססות על
ConnectionService
אחד, כמו אכיפת הגבלות טלפוניה על שיחות וידאו.
מידע נוסף זמין במאמר InCallService
.
שילוב של פתרון לשיחות
כדי לשלב את פתרון השיחות ב-Android, יש לכם את האפשרויות הבאות:
הטמעת ספריית Core-Telecom Jetpack בניהול עצמי: האפשרות הזו אידיאלית למפתחים של אפליקציות שיחה עצמאיות שלא רוצים להציג את השיחות שלהם באפליקציית הטלפון שמוגדרת כברירת מחדל, או שרוצים שהשיחות האחרות לא יוצגו בממשק המשתמש שלהם.
כשמשלבים עם ספריית Jetpack
Core-Telecom
, אפשר לעזור לאפליקציה לפעול לא רק עם שיחות טלפון במערכת במכשיר, אלא גם עם אפליקציות שיחות עצמאיות אחרות שמשתלבות עם Telecom. הספרייהCore-Telecom
מנהלת גם את הניתוב והמיקוד של האודיו. פרטים נוספים זמינים במאמר פיתוח אפליקציית שיחות.הטמעת ConnectionService API המנוהל: האפשרות הזו מאפשרת לפתח פתרון לשיחות שמסתמך על אפליקציית הטלפון הקיימת במכשיר כדי לספק את ממשק המשתמש לשיחות. דוגמאות: הטמעה של צד שלישי של שירותי שיחות SIP ו-VoIP. למידע נוסף, ראו
getDefaultDialerPackage()
.כרטיס
ConnectionService
מספק רק את האמצעים לחיבור שיחות. אין לו ממשק משתמש משויך.הטמעת InCallService ו-ConnectionService API: אפשרות זו אידיאלית אם אתם רוצים ליצור פתרון לשיחות מבוסס-
ConnectionService
משלכם, עם ממשק משתמש משלו, וגם להציג את כל השיחות האחרות ב-Android באותו ממשק משתמש. כשמשתמשים בגישה הזו, ההטמעה שלInCallService
לא יכולה להסתמך על הנחות לגבי מקורות הקריאות שהיא מציגה. בנוסף, ההטמעה שלConnectionService
צריכה להמשיך לפעול גם אם אפליקציית הטלפון שמוגדרת כברירת מחדל לא תהיהInCallService
בהתאמה אישית.
סינון שיחות
במכשירים עם Android 10 ואילך (רמת API 29 ואילך), האפליקציה יכולה לזהות שיחות ממספרים שלא נמצאים בפנקס הכתובות של המשתמש כשיחות ספאם פוטנציאליות. המשתמשים יכולים לבחור ששיחות ספאם נדחות בשקט. כדי לספק למשתמשים שקיפות רבה יותר כשהם מחמיצים שיחות, המידע על השיחות החסומים מתועד ביומן השיחות. השימוש ב-API של Android 10 מבטל את הצורך לקבל מהמשתמש את ההרשאה READ_CALL_LOG
כדי לספק את הפונקציונליות של סינון שיחות ושל זיהוי מבצע השיחה.
אתם משתמשים בהטמעה של CallScreeningService
כדי לסנן שיחות. צריך להפעיל את הפונקציה onScreenCall()
בכל שיחה נכנסת או יוצאת חדשה, אם המספר לא נמצא ברשימת אנשי הקשר של המשתמש. אפשר לבדוק את האובייקט Call.Details
כדי לקבל מידע על הקריאה. באופן ספציפי, הפונקציה getCallerNumberVerificationStatus()
כוללת מידע מספק הרשת לגבי המספר השני.
אם סטטוס האימות נכשל, סביר להניח שהשיחה הגיעה ממספר לא חוקי או שמדובר בשיחת ספאם.
Kotlin
class ScreeningService : CallScreeningService() { // This function is called when an ingoing or outgoing call // is from a number not in the user's contacts list override fun onScreenCall(callDetails: Call.Details) { // Can check the direction of the call val isIncoming = callDetails.callDirection == Call.Details.DIRECTION_INCOMING if (isIncoming) { // the handle (e.g. phone number) that the Call is currently connected to val handle: Uri = callDetails.handle // determine if you want to allow or reject the call when (callDetails.callerNumberVerificationStatus) { Connection.VERIFICATION_STATUS_FAILED -> { // Network verification failed, likely an invalid/spam call. } Connection.VERIFICATION_STATUS_PASSED -> { // Network verification passed, likely a valid call. } else -> { // Network could not perform verification. // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED. } } } } }
Java
class ScreeningService extends CallScreeningService { @Override public void onScreenCall(@NonNull Call.Details callDetails) { boolean isIncoming = callDetails.getCallDirection() == Call.Details.DIRECTION_INCOMING; if (isIncoming) { Uri handle = callDetails.getHandle(); switch (callDetails.getCallerNumberVerificationStatus()) { case Connection.VERIFICATION_STATUS_FAILED: // Network verification failed, likely an invalid/spam call. break; case Connection.VERIFICATION_STATUS_PASSED: // Network verification passed, likely a valid call. break; default: // Network could not perform verification. // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED } } } }
מגדירים את הפונקציה onScreenCall()
כך שתפעיל את respondToCall()
כדי להורות למערכת איך להגיב לשיחה החדשה. הפונקציה מקבלת פרמטר CallResponse
שאפשר להשתמש בו כדי להורות למערכת לחסום את השיחה, לדחות אותה כאילו המשתמש דחה אותה או להשתיק אותה. אפשר גם להורות למערכת לדלג על ההוספה של השיחה הזו ליומן השיחות של המכשיר.
Kotlin
// Tell the system how to respond to the incoming call // and if it should notify the user of the call. val response = CallResponse.Builder() // Sets whether the incoming call should be blocked. .setDisallowCall(false) // Sets whether the incoming call should be rejected as if the user did so manually. .setRejectCall(false) // Sets whether ringing should be silenced for the incoming call. .setSilenceCall(false) // Sets whether the incoming call should not be displayed in the call log. .setSkipCallLog(false) // Sets whether a missed call notification should not be shown for the incoming call. .setSkipNotification(false) .build() // Call this function to provide your screening response. respondToCall(callDetails, response)
Java
// Tell the system how to respond to the incoming call // and if it should notify the user of the call. CallResponse.Builder response = new CallResponse.Builder(); // Sets whether the incoming call should be blocked. response.setDisallowCall(false); // Sets whether the incoming call should be rejected as if the user did so manually. response.setRejectCall(false); // Sets whether ringing should be silenced for the incoming call. response.setSilenceCall(false); // Sets whether the incoming call should not be displayed in the call log. response.setSkipCallLog(false); // Sets whether a missed call notification should not be shown for the incoming call. response.setSkipNotification(false); // Call this function to provide your screening response. respondToCall(callDetails, response.build());
כדי שהמערכת תוכל להפעיל את ההטמעה של CallScreeningService
בצורה נכונה, צריך לרשום אותה בקובץ המניפסט עם מסנן ה-Intent וההרשאה המתאימים.
<service
android:name=".ScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService" />
</intent-filter>
</service>
הפניה מחדש של שיחה
במכשירים עם Android מגרסה 10 ואילך, ניהול כוונת השיחה שונה מאשר במכשירים עם Android מגרסה 9 ומטה. ב-Android מגרסה 10 ואילך, השידור ACTION_NEW_OUTGOING_CALL
הוצא משימוש והוחלף ב-API CallRedirectionService
. CallRedirectionService
מספק ממשקים שאפשר להשתמש בהם כדי לשנות שיחות יוצאות שמבוצעות על ידי פלטפורמת Android. לדוגמה, אפליקציות צד שלישי עשויות לבטל שיחות ולנתב אותן מחדש דרך VoIP.
Kotlin
class RedirectionService : CallRedirectionService() { override fun onPlaceCall( handle: Uri, initialPhoneAccount: PhoneAccountHandle, allowInteractiveResponse: Boolean ) { // Determine if the call should proceed, be redirected, or cancelled. val callShouldProceed = true val callShouldRedirect = false when { callShouldProceed -> { placeCallUnmodified() } callShouldRedirect -> { // Update the URI to point to a different phone number or modify the // PhoneAccountHandle and redirect. redirectCall(handle, initialPhoneAccount, true) } else -> { cancelCall() } } } }
Java
class RedirectionService extends CallRedirectionService { @Override public void onPlaceCall( @NonNull Uri handle, @NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse ) { // Determine if the call should proceed, be redirected, or cancelled. // Your app should implement this logic to determine the redirection. boolean callShouldProceed = true; boolean callShouldRedirect = false; if (callShouldProceed) { placeCallUnmodified(); } else if (callShouldRedirect) { // Update the URI to point to a different phone number or modify the // PhoneAccountHandle and redirect. redirectCall(handle, initialPhoneAccount, true); } else { cancelCall(); } } }
צריך לרשום את השירות הזה במניפסט כדי שהמערכת תוכל להפעיל אותו בצורה תקינה.
<service
android:name=".RedirectionService"
android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallRedirectionService"/>
</intent-filter>
</service>
כדי להשתמש בשירות ניתוב, האפליקציה צריכה לבקש את התפקיד 'ניתוב שיחות' מ-RoleManager
. המשתמש יתבקש לאשר לאפליקציה לטפל בהפניות של שיחות. אם לא הוקצה לאפליקציה התפקיד הזה, לא תתבצע שימוש בשירות ההפניה האוטומטית.
כדאי לבדוק אם לאפליקציה יש את התפקיד הזה כשהמשתמש מפעיל אותה, כדי שתוכלו לבקש אותו לפי הצורך. אתם מריצים כוונה שנוצרה על ידי RoleManager
, לכן חשוב לשנות את ברירת המחדל של הפונקציה onActivityResult()
כדי לטפל בבחירה של המשתמש.
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Tell the system that you want your app to handle call redirects. This // is done by using the RoleManager to register your app to handle redirects. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { val roleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager // Check if the app needs to register call redirection role. val shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) && !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION) if (shouldRequestRole) { val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION) startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE) } } } companion object { private const val REDIRECT_ROLE_REQUEST_CODE = 1 } }
Java
class MainActivity extends AppCompatActivity { private static final int REDIRECT_ROLE_REQUEST_CODE = 0; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Tell the system that you want your app to handle call redirects. This // is done by using the RoleManager to register your app to handle redirects. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE); // Check if the app needs to register call redirection role. boolean shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) && !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION); if (shouldRequestRole) { Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION); startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE); } } } }