סקירה כללית של מסגרת הטלקום

ה-Android Telecom framework (מוכר גם בשם Telecom) מנהל את האודיו שיחות וידאו במכשיר Android. כולל שיחות מבוססות-SIM, כמו שיחות שמשתמשות במסגרת טלפוניה ובקריאות VoIP שמטמיעות את ConnectionService API.

הרכיבים העיקריים שמנוהלים על ידי Telecom הם ConnectionService וגם InCallService.

הטמעה של ConnectionService משתמשת בטכנולוגיות כמו VoIP כדי להתחבר שיחות לגורמים אחרים. ההטמעה של ConnectionService הכי נפוצה הטלפון הוא מכשיר הטלפוניה ConnectionService. הוא מאפשר חיבור לשיחות דרך ספק הסלולר.

הטמעת InCallService מספקת ממשק משתמש לשיחות שמנוהלות על ידי טלקומוניקציה ומאפשרת למשתמש לשלוט בשיחות האלה ולקיים איתן אינטראקציה. במידה הרבה ביותר הטמעה נפוצה של InCallService היא האפליקציה לטלפון שבחבילה מכשיר.

הטלקום משמש כלוח בקרה. מתבצע ניתוב של שיחות ש-ConnectionService וההטמעות מספקים לממשקי המשתמש ששולחים קריאה שInCallService מספקים.

ייתכן שתרצו להטמיע את ממשקי ה-API של Telecom מהסיבות הבאות:

יצירת אפליקציה חלופית לטלפון

כדי ליצור החלפה של אפליקציית ברירת המחדל לטלפון במכשיר Android: להטמיע את ה-API של InCallService. ההטמעה צריכה לעמוד בדרישות הבאות: דרישות:

  • אסור שיהיה לו היכולת לבצע שיחות והוא צריך להיות מורכב אך ורק מהמשתמש לשיחות.
  • עליה לטפל בכל הקריאות שמסגרת הטלקום מודעת לקיומן, ולא לבצע הנחות לגבי אופי השיחות. לדוגמה, אסור להניח השיחות הן שיחות טלפוניה מבוססות-SIM, והשיחות לא כוללות הגבלות על השיחות מבוססים על כל ConnectionService, כמו אכיפת נתוני טלפוניה הגבלות על שיחות וידאו.

מידע נוסף זמין במאמר הבא: InCallService

איך משלבים פתרון לשיחות

כדי לשלב את פתרון השיחות ב-Android, צריך האפשרויות הבאות:

  • מטמיעים את ממשק ה-API של ConnectionService בניהול עצמי: האפשרות הזאת מתאימה במיוחד למפתחים של אפליקציות לשיחות עצמאיות שלא רוצים כדי להציג את השיחות שלהם באפליקציית הטלפון שמוגדרת כברירת מחדל, וגם לא יוצגו שיחות אחרות. בממשק המשתמש שלהם.

    השימוש בConnectionService בניהול עצמי עוזר לאפליקציה: לפעול באופן הדדי לא רק עם שיחות טלפוניה מקומיות מהמכשיר, אלא גם בשילוב עם אפליקציות קריאה עצמאיות אחרות שמטמיעות את ה-API הזה. בניהול עצמי ה-API של ConnectionService מנהל גם את הניתוב והמיקוד של האודיו. פרטים נוספים זמינים במאמר איך מפתחים אפליקציית שיחות

  • מטמיעים את ממשק ה-API המנוהל של ConnectionService: האפשרות הזו מאפשרת לפתח פתרון לשיחות שמסתמך על את אפליקציית הטלפון הקיימת במכשיר כדי לספק את ממשק המשתמש לשיחות. לדוגמה: הטמעת צד שלישי של שיחות SIP ושיחות VoIP שירותים שונים. פרטים נוספים זמינים במאמר getDefaultDialerPackage()

    ConnectionService לבדו מאפשר רק להתקשר. הוא לא משויך ממשק משתמש.

  • מטמיעים את ה-API של InCallService ואת ConnectionService: האפשרות הזו מתאימה אם רוצים ליצור פתרון לשיחות מבוסס ConnectionService, עם משתמש משלו ממשק משתמש, וגם להציג את כל שיחות Android האחרות באותו ממשק משתמש. כשמשתמשים בגישה הזו, אסור שההטמעה של InCallService תתבצע מניחות הנחות לגבי מקורות השיחות שהיא מציגה. כמו כן, ההטמעה של ConnectionService חייבת להמשיך לפעול בלי אפליקציית ברירת המחדל לטלפון, שהוגדרה לInCallService בהתאמה אישית.

סינון שיחות

מכשירים עם Android מגרסה 10 ומעלה (רמת API 29) ואילך מאפשרים לאפליקציה שלך לזהות שיחות ממספרים שלא מופיעים בפנקס הכתובות של המשתמש כספאם פוטנציאלי שיחות. משתמשים יכולים לבחור לדחות שיחות ספאם באופן שקט. כדי לספק שקיפות למשתמשים כאשר הם מחמיצים שיחות, מידע על שיחות חסומות השיחות מתועדות ביומן השיחות. השימוש ב-Android 10 API מבטל את כדי לקבל 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 10 ואילך מכשירים עם Android מגרסה 9 ומטה. ב-Android מגרסה 10 ואילך, ACTION_NEW_OUTGOING_CALL הוצא משימוש והוחלף CallRedirectionService API. 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. הפעולה הזו תבקש את המשתמש אם הוא רוצה לאפשר לאפליקציה לטפל בהפניות אוטומטיות של שיחות. אם האפליקציה לא קיבל את התפקיד הזה, לא נעשה שימוש בשירות ההפניה האוטומטית שלך.

צריך לבדוק אם לאפליקציה יש את התפקיד הזה כשהמשתמש מפעיל אותה. אפשר לבקש אותו לפי הצורך. אתם מפעילים Intent שנוצר על ידי 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);
            }
        }
    }
}