Core-Telecom

Library Core-Telecom menyederhanakan proses mengintegrasikan aplikasi panggilan Anda dengan platform Android dengan menyediakan serangkaian API yang andal dan konsisten

Jika ingin menjelajahi penerapan praktis, Anda dapat menemukan aplikasi contoh di GitHub:

Menyiapkan Core-Telecom

Tambahkan dependensi androidx.core:core-telecom ke file build.gradle aplikasi Anda:

dependencies {
    implementation ("androidx.core:core-telecom:1.0.0")
}

Deklarasikan izin MANAGE_OWN_CALLS di AndroidManifest.xml Anda:

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

Daftarkan aplikasi Anda

Daftarkan aplikasi panggilan Anda dengan Android menggunakan CallsManager untuk mulai menambahkan panggilan ke sistem. Saat mendaftar, tentukan kemampuan aplikasi Anda (misalnya, dukungan audio, video):

val callsManager = CallsManager(context)

val capabilities: @CallsManager.Companion.Capability Int =
    (CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)

callsManager.registerAppWithTelecom(capabilities)

Pengelolaan Panggilan Telepon

Gunakan Core-Telecom API untuk membuat dan mengelola siklus proses panggilan.

Membuat panggilan

Objek CallAttributesCompat menentukan properti panggilan unik, yang dapat memiliki karakteristik berikut:

  • displayName: nama penelepon.
  • address: Alamat panggilan (misalnya, nomor telepon, link rapat).
  • direction: Masuk atau keluar.
  • callType: Audio atau video.
  • callCapabilities: Mendukung transfer dan penahanan.

Berikut adalah contoh cara membuat panggilan masuk:

fun createIncomingCallAttributes(
    callerName: String,
    callerNumber: String,
    isVideoCall: Boolean): CallAttributesCompat {
    val addressUri = Uri.parse("YourAppScheme:$callerNumber")

    // Define capabilities supported by your call.
    val callCapabilities = CallAttributesCompat.CallCapability(
        supportsSetInactive = CallAttributesCompat.SUPPORTS_SET_INACTIVE // Call can be made inactive (implies hold)
    )

    return CallAttributesCompat(
        displayName = callerName,
        address = addressUri,
        direction = CallAttributesCompat.DIRECTION_INCOMING,
        callType = if (isVideoCall) CallAttributesCompat.CALL_TYPE_VIDEO_CALL else CallAttributesCompat.CALL_TYPE_AUDIO_CALL,
        callCapabilitiesCompat = callCapabilities
    )
}

Menambahkan panggilan

Gunakan callsManager.addCall dengan CallAttributesCompat dan callback untuk menambahkan panggilan baru ke sistem dan mengelola update platform jarak jauh. callControlScope dalam blok addCall terutama memungkinkan aplikasi Anda melakukan transisi status panggilan dan menerima update audio:

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onAnswerCall, // Watch needs to know if it can answer the call.
        onSetCallDisconnected,
        onSetCallActive,
        onSetCallInactive
    ) {
        // The call was successfully added once this scope runs.
        callControlScope = this
    }
}
catch(addCallException: Exception){
   // Handle the addCall failure.
}

Menjawab telepon

Menjawab panggilan masuk dalam CallControlScope:

when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> { /* Call answered */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

Menolak panggilan

Menolak panggilan menggunakan disconnect() dengan DisconnectCause.REJECTED dalam CallControlScope:

disconnect(DisconnectCause(DisconnectCause.REJECTED))

Mengaktifkan panggilan keluar

Menetapkan panggilan keluar menjadi aktif setelah pihak jarak jauh menjawab:

when (val result = setActive()) {
    is CallControlResult.Success -> { /* Call active */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

Menahan panggilan

Gunakan setInactive() untuk menahan panggilan:

when (val result = setInactive()) {
    is CallControlResult.Success -> { /* Call on hold */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

Mengakhiri panggilan

Mengakhiri panggilan menggunakan disconnect() dengan DisconnectCause:

disconnect(DisconnectCause(DisconnectCause.LOCAL))

Mengelola endpoint audio panggilan

Amati dan kelola endpoint audio menggunakan currentCallEndpoint, availableEndpoints, dan isMuted Flow dalam CallControlScope

fun observeAudioStateChanges(callControlScope: CallControlScope) {
    with(callControlScope) {
        launch { currentCallEndpoint.collect { /* Update UI */ } }
        launch { availableEndpoints.collect { /* Update UI */ } }
        launch { isMuted.collect { /* Handle mute state */ } }
    }
}

Ubah perangkat audio aktif menggunakan requestEndpointChange():

coroutineScope.launch {
     callControlScope.requestEndpointChange(callEndpoint)
}

Dukungan latar depan

Library ini menggunakan ConnectionService (Android 13 level API 33 dan yang lebih rendah) atau foregroundtypes (Android 14 level API 34 dan yang lebih tinggi) untuk dukungan latar depan.

Sebagai bagian dari persyaratan latar depan, aplikasi harus memposting notifikasi agar pengguna mengetahui bahwa aplikasi sedang berjalan di latar depan.

Untuk memastikan aplikasi Anda mendapatkan prioritas eksekusi di latar depan, buat notifikasi setelah Anda menambahkan panggilan dengan platform. Prioritas latar depan dihapus saat aplikasi Anda mengakhiri panggilan atau notifikasi Anda tidak lagi valid.

Pelajari lebih lanjut layanan latar depan.

Dukungan Surface jarak jauh

Perangkat jarak jauh (smartwatch, headset Bluetooth, Android Auto) dapat mengelola panggilan tanpa interaksi langsung dengan ponsel. Aplikasi Anda harus menerapkan lambda callback (onAnswerCall, onSetCallDisconnected, onSetCallActive, onSetCallInactive) yang diberikan ke CallsManager.addCall untuk menangani tindakan yang dimulai oleh perangkat ini.

Saat tindakan jarak jauh terjadi, lambda yang sesuai akan dipanggil.

Penyelesaian lambda yang berhasil menandakan bahwa perintah telah diproses. Jika perintah tidak dapat dipatuhi, lambda akan memunculkan pengecualian.

Penerapan yang tepat memastikan kontrol panggilan yang lancar di berbagai perangkat. Uji secara menyeluruh dengan berbagai platform jarak jauh.

Ekstensi Panggilan

Selain mengelola status panggilan dan rute audio panggilan, library ini juga mendukung ekstensi panggilan, yang merupakan fitur opsional yang dapat diterapkan aplikasi Anda untuk pengalaman panggilan yang lebih kaya di platform jarak jauh, seperti Android Auto. Fitur ini mencakup ruang pertemuan, senyapkan panggilan, dan ikon panggilan tambahan. Saat aplikasi Anda menerapkan ekstensi, informasi yang diberikan aplikasi akan disinkronkan dengan semua perangkat terhubung yang juga mendukung tampilan ekstensi ini di UI-nya. Artinya, fitur ini juga akan tersedia di perangkat jarak jauh agar pengguna dapat berinteraksi.

Membuat Panggilan dengan Ekstensi

Saat membuat panggilan, alih-alih menggunakan CallManager#addCall untuk membuat panggilan, Anda dapat menggunakan CallManager#addCallWithExtensions, yang memberi aplikasi akses ke cakupan berbeda yang disebut ExtensionInitializationScope. Cakupan ini memungkinkan aplikasi menginisialisasi serangkaian ekstensi opsional yang didukungnya. Selain itu, cakupan ini menyediakan metode tambahan, onCall, yang menyediakan CallControlScope kembali ke aplikasi setelah pertukaran dan inisialisasi kemampuan ekstensi selesai.

scope.launch {
    mCallsManager.addCallWithExtensions(
        attributes,
        onAnswer,
        onDisconnect,
        onSetActive,
        onSetInactive
    ) {
        // Initialize extension-specific code...

        // After the call has been initialized, perform in-call actions
        onCall {
            // Example: process call state updates
            callStateFlow.onEach { newState ->
                // handle call state updates and notify telecom
            }.launchIn(this)

            // Use initialized extensions...
        }
    }
}

Mendukung Peserta Panggilan

Jika aplikasi Anda mendukung peserta panggilan untuk rapat atau panggilan grup, gunakan addParticipantExtension untuk menyatakan dukungan untuk ekstensi ini dan gunakan API terkait untuk memperbarui platform jarak jauh saat peserta berubah.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Notifies Jetpack that this app supports the participant
        // extension and provides the initial participants state in the call.
        val participantExtension = addParticipantExtension(
            initialParticipants,
            initialActiveParticipant
        )

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // Example: update remote surfaces when the call participants change
            participantsFlow.onEach { newParticipants ->
                participantExtension.updateParticipants(newParticipants)
            }.launchIn(this)
        }
    }

Selain memberi tahu platform jarak jauh tentang peserta yang ada dalam panggilan, peserta aktif juga dapat diperbarui menggunakan ParticipantExtension#updateActiveParticipant.

Ada juga dukungan untuk tindakan opsional terkait peserta panggilan. Aplikasi dapat menggunakan ParticipantExtension#addRaiseHandSupport untuk mendukung konsep peserta mengangkat tangan dalam panggilan dan melihat peserta lain yang juga mengangkat tangan.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Notifies Jetpack that this app supports the participant
        // extension and provides the initial list of participants in the call.
        val participantExtension = addParticipantExtension(initialParticipants)
        // Notifies Jetpack that this app supports the notion of participants
        // being able to raise and lower their hands.
        val raiseHandState = participantExtension.addRaiseHandSupport(
                initialRaisedHands
            ) { onHandRaisedStateChanged ->
                // handle this user's raised hand state changed updates from
                // remote surfaces.
            }

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // Example: update remote surfaces when the call participants change
            participantsFlow.onEach { newParticipants ->
                participantExtension.updateParticipants(newParticipants)
            }.launchIn(this)
            // notify remote surfaces of which of the participants have their
            // hands raised
            raisedHandsFlow.onEach { newRaisedHands ->
                raiseHandState.updateRaisedHands(newRaisedHands)
            }.launchIn(this)
        }
    }

Mendukung Senyapkan Panggilan

Peredaman panggilan memungkinkan pengguna meminta aplikasi untuk meredam audio panggilan keluar tanpa mematikan mikrofon perangkat secara fisik. Fitur ini dikelola per panggilan, sehingga Jetpack menangani kompleksitas pengelolaan status senyap global panggilan seluler yang sedang berlangsung saat panggilan VOIP aktif. Hal ini membuat membisukan audio keluar lebih sedikit rentan terhadap error dalam skenario multi-panggilan sekaligus memungkinkan fitur yang berguna seperti indikasi "apakah Anda sedang berbicara" saat pengguna sedang berbicara tanpa menyadari bahwa pembisuan panggilan diaktifkan.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Add support for locally silencing the call's outgoing audio and
        // register a handler for when the user changes the call silence state
        // from a remote surface.
        val callSilenceExtension = addLocalCallSilenceExtension(
            initialCallSilenceState = false
        ) { newCallSilenceStateRequest ->
            // handle the user's request to enable/disable call silence from
            // a remote surface
        }

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // When the call's call silence state changes, update remote
            // surfaces of the new state.
            callSilenceState.onEach { isSilenced ->
                callSilenceExtension.updateIsLocallySilenced(isSilenced)
            }.launchIn(this)
        }
    }

Ikon Panggilan Dukungan

Ikon panggilan memungkinkan aplikasi menentukan ikon kustom yang merepresentasikan panggilan yang akan ditampilkan di platform jarak jauh selama panggilan. Ikon ini juga dapat diperbarui selama masa aktif panggilan.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Add support for a custom call icon to be displayed during the
        // lifetime of the call.
        val callIconExtension = addCallIconExtension(
            initialCallIconUri = initialUri
        )

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // When the call's icon changes, update remote surfaces by providing
            // the new URI.
            callIconUri.onEach { newIconUri ->
                callIconExtension.updateCallIconUri(newIconUri)
            }.launchIn(this)
        }
    }

Mengintegrasikan dengan log panggilan sistem

Core-Telecom memungkinkan aplikasi Anda terintegrasi dengan log panggilan sistem. Fitur ini memungkinkan panggilan yang dilakukan atau diterima menggunakan aplikasi VoIP Anda muncul di histori panggilan aplikasi telepon sistem, sehingga pengguna dapat menggunakan aplikasi telepon untuk memulai panggilan balik.

Mengaktifkan integrasi log panggilan

Untuk mengaktifkan fitur ini, aplikasi Anda harus mendaftar untuk menangani intent TelecomManager.ACTION_CALL_BACK di AndroidManifest.xml dan memberikan Activity untuk memproses callback. Misalnya, jika VoipCallbackActivity adalah aktivitas yang menangani callback, manifes Anda akan terlihat seperti:

Mendaftarkan intent dalam manifes

<activity
    android:name=".VoipCallbackActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.telecom.action.CALL_BACK" />
    </intent-filter>
</activity>

Menangani intent callback

Saat pengguna memulai panggilan balik dari dialer sistem, aktivitas terdaftar Anda diluncurkan dengan tindakan TelecomManager.ACTION_CALL_BACK. Intent berisi TelecomManager.EXTRA_UUID yang diberikan ke aplikasi Anda saat Anda pertama kali menambahkan panggilan menggunakan CallsManager.

Metode CallControlScope getCallId menampilkan ID unik ini untuk panggilan.

Anda harus menyimpan semua informasi yang diperlukan untuk memulai panggilan yang diidentifikasi oleh UUID di dalam aplikasi Anda (misalnya, daftar nomor telepon untuk panggilan grup) sehingga Anda dapat merekonstruksi dan melakukan panggilan lagi saat callback dipicu.

class VoipCallbackActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (intent?.action == TelecomManager.ACTION_CALL_BACK) {
            val uuidString = intent.getStringExtra(TelecomManager.EXTRA_UUID)
            if (uuidString != null) {
                val uuid = UUID.fromString(uuidString)
                
                // Retrieve your persisted call info using the UUID
                val callData = CallRepository.getCallByUuid(uuid)
                
                if (callData != null) {
                    // Place the call again using your app's logic
                    // ...
                }
            }
        }
        finish()
    }
}

Memilih tidak ikut integrasi log panggilan

Secara default, panggilan dicatat ke log panggilan sistem. Anda dapat menonaktifkan fitur ini berdasarkan per panggilan dengan menyetel parameter isLogged ke false saat membuat CallAttributesCompat:

val callAttributes = CallAttributesCompat(
    displayName = callerName,
    address = addressUri,
    direction = CallAttributesCompat.DIRECTION_OUTGOING,
    isLogExcluded = true // Opt-out of system call logging
)