The Core-Telecom
library streamlines the process of integrating your calling
application with the Android platform by providing a robust and consistent set
of APIs
If you want to explore practical implementations, you can find sample applications on GitHub:
- Lightweight Sample App — A minimal example
demonstrating
Core-Telecom
API usage. Ideal for quickly understanding fundamental concepts. - Comprehensive Sample App (Developed by the Core-Telecom Team) — A more feature-rich application showcasing advanced Telecom functionalities and best practices. This is a great resource for understanding complex integration scenarios.
Set up Core-Telecom
Add the androidx.core:core-telecom
dependency to your app's build.gradle
file:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
Declare the MANAGE_OWN_CALLS
permission in your AndroidManifest.xml
:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
Register your app
Register your calling app with Android using CallsManager
to begin adding
calls to the system. When registering, specify your app's capabilities (for
example, audio, video support):
val callsManager = CallsManager(context)
val capabilities: @CallsManager.Companion.Capability Int =
(CallsManager.CAPABILITY_BASELINE or
CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)
callsManager.registerAppWithTelecom(capabilities)
Call Management
Use Core-Telecom APIs to create and manage a call lifecycle.
Create a call
The CallAttributesCompat
object defines the properties of a unique call,
which can have the following characteristics:
displayName
: caller name.address
: Call address (for example, phone number, meeting link).direction
: Incoming or outgoing.callType
: Audio or video.callCapabilities
: Supports transfer and hold.
Here's an example of how to create an incoming call:
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
)
}
Add a call
Use callsManager.addCall
with CallAttributesCompat
and callbacks to add a
new call to the system and manage remote surface updates. The callControlScope
within the addCall
block primarily allows your app to transition the call
state and receive audio updates:
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.
}
Answer a call
Answer an incoming call within the CallControlScope
:
when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
is CallControlResult.Success -> { /* Call answered */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Reject a call
Reject a call using disconnect()
with DisconnectCause.REJECTED
within the
CallControlScope
:
disconnect(DisconnectCause(DisconnectCause.REJECTED))
Make an outgoing call active
Set an outgoing call to active once the remote party answers:
when (val result = setActive()) {
is CallControlResult.Success -> { /* Call active */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Place a call on hold
Use setInactive()
to put a call on hold:
when (val result = setInactive()) {
is CallControlResult.Success -> { /* Call on hold */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Disconnect a call
Disconnect a call using disconnect()
with a DisconnectCause
:
disconnect(DisconnectCause(DisconnectCause.LOCAL))
Manage call audio endpoints
Observe and manage audio endpoints using currentCallEndpoint
,
availableEndpoints
, and isMuted
Flow
s within the CallControlScope
fun observeAudioStateChanges(callControlScope: CallControlScope) {
with(callControlScope) {
launch { currentCallEndpoint.collect { /* Update UI */ } }
launch { availableEndpoints.collect { /* Update UI */ } }
launch { isMuted.collect { /* Handle mute state */ } }
}
}
Change the active audio device using requestEndpointChange()
:
coroutineScope.launch {
callControlScope.requestEndpointChange(callEndpoint)
}
Foreground support
The library uses ConnectionService
(Android 13 API level 33 and lower) or
foregroundtypes (Android 14 API level 34 and higher) for foreground
support.
As part of the foreground requirements, the application must post a notification for users to know that the application is running in the foreground.
To ensure that your app gets foreground execution priority, create a notification once you add the call with the platform. Foreground priority is removed when your app terminates the call or your notification is no longer valid.
Learn more about foreground services.
Remote Surface support
Remote devices (smartwatches, Bluetooth headsets, Android Auto) are capable of
call management without direct phone interaction. Your app must implement
callback lambdas (onAnswerCall
, onSetCallDisconnected
, onSetCallActive
,
onSetCallInactive
) provided to CallsManager.addCall
to handle actions
initiated by these devices.
When a remote action occurs, the corresponding lambda is invoked.
Successful completion of the lambda signals that the command was processed. If the command cannot be obeyed, the lambda should throw an exception.
Proper implementation ensures seamless call control across different devices. Test thoroughly with various remote surfaces.