Die Core-Telecom-Bibliothek vereinfacht die Integration Ihrer Anrufanwendung in die Android-Plattform, da sie eine robuste und konsistente Reihe von APIs bietet.
Wenn Sie sich praktische Implementierungen ansehen möchten, finden Sie Beispielanwendungen auf GitHub:
- Lightweight Sample App: Ein minimales Beispiel für die Verwendung der
Core-TelecomAPI. Ideal, um schnell grundlegende Konzepte zu verstehen. - Umfassende Beispiel-App (entwickelt vom Core-Telecom-Team): Eine funktionsreichere Anwendung, die erweiterte Telekommunikationsfunktionen und Best Practices demonstriert. Hier finden Sie Informationen zu komplexen Integrationsszenarien.
Core-Telecom einrichten
Fügen Sie der Datei build.gradle Ihrer App die Abhängigkeit androidx.core:core-telecom hinzu:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
Erklären Sie die Berechtigung MANAGE_OWN_CALLS in Ihrer AndroidManifest.xml:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
App registrieren
Registrieren Sie Ihre Anruf-App mit CallsManager bei Android, um mit dem Hinzufügen von Anrufen zum System zu beginnen. Geben Sie bei der Registrierung die Funktionen Ihrer App an, z. B. Audio- oder Videounterstützung:
val callsManager = CallsManager(context)
val capabilities: @CallsManager.Companion.Capability Int =
(CallsManager.CAPABILITY_BASELINE or
CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)
callsManager.registerAppWithTelecom(capabilities)
Anrufverwaltung
Mit den Core-Telecom-APIs können Sie einen Anrufslebenszyklus erstellen und verwalten.
Anruf starten
Das CallAttributesCompat-Objekt definiert die Eigenschaften eines eindeutigen Anrufs, der die folgenden Merkmale haben kann:
displayName: Name des Anrufers.address: Anrufadresse (z. B. Telefonnummer, Videokonferenzlink).direction: Eingehend oder ausgehend.callType: Audio oder Video.callCapabilities: Unterstützt die Übertragung und das Halten.
Hier ein Beispiel für das Erstellen eines eingehenden Anrufs:
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
)
}
Anruf hinzufügen
Verwenden Sie callsManager.addCall mit CallAttributesCompat und Callbacks, um dem System einen neuen Aufruf hinzuzufügen und Updates für Remote-Oberflächen zu verwalten. Die callControlScope im addCall-Block ermöglicht Ihrer App in erster Linie, den Anrufstatus zu ändern und Audio-Updates zu empfangen:
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.
}
Anruf entgegennehmen
So nehmen Sie einen eingehenden Anruf in CallControlScope an:
when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
is CallControlResult.Success -> { /* Call answered */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Anruf ablehnen
So lehnen Sie einen Anruf mit disconnect() und DisconnectCause.REJECTED innerhalb von CallControlScope ab:
disconnect(DisconnectCause(DisconnectCause.REJECTED))
Ausgehenden Anruf aktivieren
Ausgehenden Anruf als aktiv festlegen, sobald die Gegenstelle antwortet:
when (val result = setActive()) {
is CallControlResult.Success -> { /* Call active */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Halten eines Anrufs
So halten Sie einen Anruf mit setInactive():
when (val result = setInactive()) {
is CallControlResult.Success -> { /* Call on hold */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Anruf trennen
So trennen Sie einen Anruf mit disconnect() über eine DisconnectCause:
disconnect(DisconnectCause(DisconnectCause.LOCAL))
Audio-Endpunkte für Anrufe verwalten
Audio-Endpunkte mit currentCallEndpoint, availableEndpoints und isMuted Flows in der CallControlScope beobachten und verwalten
fun observeAudioStateChanges(callControlScope: CallControlScope) {
with(callControlScope) {
launch { currentCallEndpoint.collect { /* Update UI */ } }
launch { availableEndpoints.collect { /* Update UI */ } }
launch { isMuted.collect { /* Handle mute state */ } }
}
}
So ändern Sie das aktive Audiogerät mit requestEndpointChange():
coroutineScope.launch {
callControlScope.requestEndpointChange(callEndpoint)
}
Unterstützung im Vordergrund
Die Bibliothek verwendet ConnectionService (Android 13, API‑Level 33 und niedriger) oder foregroundtypes (Android 14, API‑Level 34 und höher) für die Unterstützung im Vordergrund.
Im Rahmen der Anforderungen für den Vordergrund muss die Anwendung eine Benachrichtigung für Nutzer senden, damit diese wissen, dass die Anwendung im Vordergrund ausgeführt wird.
Damit Ihre App Vorrang bei der Ausführung im Vordergrund erhält, erstellen Sie eine Benachrichtigung, sobald Sie den Anruf mit der Plattform hinzufügen. Die Vordergrundpriorität wird entfernt, wenn Ihre App den Anruf beendet oder Ihre Benachrichtigung nicht mehr gültig ist.
Weitere Informationen zu Vordergrunddiensten
Remote-Support für Surface
Externe Geräte (Smartwatches, Bluetooth-Headsets, Android Auto) können Anrufe verwalten, ohne dass eine direkte Interaktion mit dem Smartphone erforderlich ist. Ihre App muss Callback-Lambdas (onAnswerCall, onSetCallDisconnected, onSetCallActive, onSetCallInactive) implementieren, die an CallsManager.addCall übergeben werden, um Aktionen zu verarbeiten, die von diesen Geräten initiiert werden.
Wenn eine Remote-Aktion ausgeführt wird, wird das entsprechende Lambda aufgerufen.
Wenn die Lambda-Funktion erfolgreich abgeschlossen wurde, bedeutet das, dass der Befehl verarbeitet wurde. Wenn der Befehl nicht ausgeführt werden kann, sollte das Lambda eine Ausnahme auslösen.
Eine korrekte Implementierung sorgt für eine nahtlose Anrufsteuerung auf verschiedenen Geräten. Testen Sie die Fernbedienung gründlich mit verschiedenen Oberflächen.
Anruferweiterungen
Neben der Verwaltung des Anrufstatus und der Audio-Weiterleitung Ihrer Anrufe unterstützt die Bibliothek auch Anruferweiterungen. Das sind optionale Funktionen, die Ihre App implementieren kann, um Anrufe auf Remote-Oberflächen wie Android Auto zu optimieren. Dazu gehören Konferenzräume, das Stummschalten von Anrufen und zusätzliche Anrufsymbole. Wenn Ihre App eine Erweiterung implementiert, werden die von der App bereitgestellten Informationen mit allen verbundenen Geräten synchronisiert, die diese Erweiterungen auch in ihrer Benutzeroberfläche unterstützen. Das bedeutet, dass diese Funktionen auch auf Remote-Geräten verfügbar sind, damit Nutzer mit ihnen interagieren können.
Anruf mit Erweiterungen starten
Wenn Sie einen Anruf erstellen, können Sie anstelle von CallManager#addCall die Methode CallManager#addCallWithExtensions verwenden. Dadurch erhält die App Zugriff auf einen anderen Bereich namens ExtensionInitializationScope. Mit diesem Bereich kann die Anwendung die Gruppe der optionalen Erweiterungen initialisieren, die sie unterstützt. Außerdem bietet dieser Bereich eine zusätzliche Methode, onCall, die nach dem Austausch und der Initialisierung der Erweiterungsfunktion einen CallControlScope an die App zurückgibt.
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...
}
}
}
Teilnehmer an Supportanrufen
Wenn Ihre App Anrufteilnehmer für Videokonferenzen oder Gruppenanrufe unterstützt, verwenden Sie addParticipantExtension, um die Unterstützung für diese Erweiterung zu deklarieren, und die zugehörigen APIs, um Remote-Oberflächen zu aktualisieren, wenn sich die Teilnehmer ändern.
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)
}
}
Neben der Benachrichtigung von Remote-Oberflächen darüber, welche Teilnehmer am Anruf teilnehmen, kann der aktive Teilnehmer auch mit ParticipantExtension#updateActiveParticipant aktualisiert werden.
Außerdem werden optionale Aktionen im Zusammenhang mit den Anrufteilnehmern unterstützt.
Die App kann ParticipantExtension#addRaiseHandSupport verwenden, um die Funktion „Melden“ zu unterstützen. So können Teilnehmer sehen, welche anderen Teilnehmer sich ebenfalls gemeldet haben.
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)
}
}
Supportanrufe stummschalten
Mit der Funktion „Anruf stummschalten“ kann ein Nutzer anfordern, dass die App die ausgehende Audioausgabe eines Anrufs stummschaltet, ohne das Mikrofon des Geräts physisch stummzuschalten. Diese Funktion wird pro Anruf verwaltet. Jetpack übernimmt die Komplexität der Verwaltung des globalen Stummschaltungsstatus laufender Mobilfunkanrufe, während ein VOIP-Anruf aktiv ist. Dadurch wird das Stummschalten von ausgehendem Audio in Szenarien mit mehreren Anrufen weniger fehleranfällig. Außerdem können hilfreiche Funktionen wie „Sprichst du gerade?“ angezeigt werden, wenn der Nutzer spricht, ohne zu wissen, dass die Stummschaltung aktiviert ist.
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)
}
}
Symbole für Supportanrufe
Mit einem Anrufsymbol kann die App ein benutzerdefiniertes Symbol angeben, das während des Anrufs auf Remote-Oberflächen angezeigt werden soll. Dieses Symbol kann auch während des Anrufs aktualisiert werden.
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)
}
}
Integration in das Systemanrufprotokoll
Mit Core-Telecom kann Ihre Anwendung in die Systemanrufliste eingebunden werden. Mit dieser Funktion können Anrufe, die mit Ihrer VoIP-Anwendung getätigt oder empfangen wurden, im Anrufverlauf des System-Dialers angezeigt werden. Nutzer können den Dialer verwenden, um einen Rückruf zu starten.
Anruflistenintegration aktivieren
Damit diese Funktion aktiviert werden kann, muss Ihre Anwendung registriert sein, um den Intent TelecomManager.ACTION_CALL_BACK in Ihrem AndroidManifest.xml zu verarbeiten, und einen Activity für die Verarbeitung des Callbacks bereitstellen. Wenn beispielsweise VoipCallbackActivity die Aktivität ist, die den Callback verarbeitet, sollte Ihr Manifest so aussehen:
Intent im Manifest registrieren
<activity
android:name=".VoipCallbackActivity"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.action.CALL_BACK" />
</intent-filter>
</activity>
Rückruf-Intent verarbeiten
Wenn der Nutzer einen Rückruf über die Systemwählfunktion initiiert, wird Ihre registrierte Aktivität mit der Aktion TelecomManager.ACTION_CALL_BACK gestartet.
Der Intent enthält TelecomManager.EXTRA_UUID, das Ihrer App bereitgestellt wird, wenn Sie den Anruf ursprünglich mit CallsManager hinzugefügt haben.
Die Methode CallControlScope getCallId gibt diese eindeutige ID für den Aufruf zurück.
Sie sollten alle Informationen, die zum Starten des Anrufs mit der UUID erforderlich sind, in Ihrer App speichern (z. B. eine Liste von Telefonnummern für einen Gruppenanruf), damit Sie den Anruf rekonstruieren und noch einmal starten können, wenn der Callback ausgelöst wird.
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()
}
}
Integration der Anrufliste deaktivieren
Standardmäßig werden Anrufe im Systemanrufprotokoll protokolliert. Sie können diese Funktion für jeden einzelnen Aufruf deaktivieren, indem Sie beim Erstellen von CallAttributesCompat den Parameter isLogged auf false setzen:
val callAttributes = CallAttributesCompat(
displayName = callerName,
address = addressUri,
direction = CallAttributesCompat.DIRECTION_OUTGOING,
isLogExcluded = true // Opt-out of system call logging
)