Biblioteka Core-Telecom upraszcza proces integrowania aplikacji do dzwonienia z platformą Android, udostępniając niezawodny i spójny zestaw interfejsów API.
Jeśli chcesz poznać praktyczne zastosowania, przykładowe aplikacje znajdziesz na GitHubie:
- Prosta aplikacja przykładowa – minimalny przykład pokazujący użycie interfejsu
Core-TelecomAPI. Idealne do szybkiego zrozumienia podstawowych pojęć. - Kompleksowa aplikacja przykładowa (opracowana przez zespół Core-Telecom) – bardziej rozbudowana aplikacja prezentująca zaawansowane funkcje telekomunikacyjne i sprawdzone metody. To świetne źródło informacji o złożonych scenariuszach integracji.
Konfigurowanie Core-Telecom
Dodaj zależność androidx.core:core-telecom do pliku build.gradle aplikacji:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
Zadeklaruj uprawnienie MANAGE_OWN_CALLS w pliku AndroidManifest.xml:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
Rejestracja swojej aplikacji
Zarejestruj aplikację do połączeń w Androidzie za pomocą CallsManager, aby zacząć dodawać połączenia do systemu. Podczas rejestracji określ możliwości aplikacji (np. obsługę dźwięku i wideo):
val callsManager = CallsManager(context)
val capabilities: @CallsManager.Companion.Capability Int =
(CallsManager.CAPABILITY_BASELINE or
CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)
callsManager.registerAppWithTelecom(capabilities)
Zarządzanie połączeniami
Używaj interfejsów Core-Telecom API do tworzenia cyklu życia połączenia i zarządzania nim.
Tworzenie połączenia
Obiekt CallAttributesCompat określa właściwości unikalnego połączenia, które może mieć te cechy:
displayName: nazwa dzwoniącego.address: adres połączenia (np. numer telefonu, link do spotkania);direction: Przychodzące lub wychodzące.callType: dźwięk lub obraz.callCapabilities: obsługuje przekazywanie i zawieszanie połączeń.
Oto przykład tworzenia połączenia przychodzącego:
fun createIncomingCallAttributes(
callerName: String,
callerNumber: String,
isVideoCall: Boolean): CallAttributesCompat {
val addressUri = Uri.parse("YourAppScheme:$callerNumber")
return CallAttributesCompat(
displayName = callerName,
address = addressUri,
direction = CallAttributesCompat.DIRECTION_INCOMING,
callType = if (isVideoCall) {
CallAttributesCompat.CALL_TYPE_VIDEO_CALL
} else {
CallAttributesCompat.CALL_TYPE_AUDIO_CALL
},
callCapabilities = CallAttributesCompat.SUPPORTS_SET_INACTIVE
)
}
Dodawanie połączenia
Używaj callsManager.addCall z CallAttributesCompat i wywołaniami zwrotnymi, aby dodać nowe wywołanie do systemu i zarządzać aktualizacjami zdalnych obszarów. Blok callControlScopew addCall umożliwia przede wszystkim zmianę stanu połączenia i otrzymywanie aktualizacji dźwięku:
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.
}
Odbieranie połączenia
Odbierz połączenie przychodzące w ciągu CallControlScope:
when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
is CallControlResult.Success -> { /* Call answered */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Odrzucanie połączenia
Odrzuć połączenie za pomocą disconnect() z DisconnectCause.REJECTED w CallControlScope:
disconnect(DisconnectCause(DisconnectCause.REJECTED))
Aktywowanie połączenia wychodzącego
Ustawianie połączenia wychodzącego jako aktywnego po odebraniu go przez rozmówcę:
when (val result = setActive()) {
is CallControlResult.Success -> { /* Call active */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Przełączanie połączenia w stan oczekiwania
Użyj numeru setInactive(), aby zawiesić połączenie:
when (val result = setInactive()) {
is CallControlResult.Success -> { /* Call on hold */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Rozłączanie połączenia
Aby rozłączyć połączenie za pomocą disconnect() i DisconnectCause:
disconnect(DisconnectCause(DisconnectCause.LOCAL))
Zarządzanie punktami końcowymi audio połączeń
Obserwuj punkty końcowe audio i zarządzaj nimi za pomocą currentCallEndpoint, availableEndpoints i isMuted Flow w CallControlScope. Nie używaj interfejsów AudioManager#setCommunicationDevice ani AudioManager#startBluetoothSco API do zarządzania trasami audio podczas korzystania z platformy telekomunikacyjnej, ponieważ spowoduje to problemy z dźwiękiem podczas połączenia.
fun observeAudioStateChanges(callControlScope: CallControlScope) {
with(callControlScope) {
launch { currentCallEndpoint.collect { /* Update UI */ } }
launch { availableEndpoints.collect { /* Update UI */ } }
launch { isMuted.collect { /* Handle mute state */ } }
}
}
Zmień aktywne urządzenie audio za pomocą requestEndpointChange():
coroutineScope.launch {
callControlScope.requestEndpointChange(callEndpoint)
}
Obsługa pierwszego planu
Biblioteka używa ConnectionService na Androidzie 13 (API na poziomie 33) i starszych wersjach lub typów usług na pierwszym planie na Androidzie 14 (API na poziomie 34) i nowszych wersjach do obsługi pierwszego planu.
Aby połączenia były aktywne, gdy aplikacja działa w tle, umieść CallsManager w usłudze na pierwszym planie Service (np. LifecycleService) i zadeklaruj typ usługi na pierwszym planie phoneCall w AndroidManifest.xml:
<service
android:name=".TelecomVoipService"
android:foregroundServiceType="phoneCall" />
Zgodnie z wymaganiami dotyczącymi pierwszego planu aplikacja musi wysyłać powiadomienie NotificationCompat.CallStyle, aby użytkownicy wiedzieli, że połączenie jest aktywne na pierwszym planie. Aby zapewnić aplikacji priorytet wykonywania na pierwszym planie, przenieś usługę na pierwszy plan za pomocą startForeground po dodaniu wywołania z platformą:
startForeground(
notificationId,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
)
Więcej informacji o usługach na pierwszym planie
Zdalna pomoc dotycząca urządzeń Surface
Urządzenia zdalne (smartwatche, zestawy słuchawkowe Bluetooth, Android Auto) mogą zarządzać połączeniami bez bezpośredniej interakcji z telefonem. Aplikacja musi implementować lambdy wywołania zwrotnego (onAnswerCall, onSetCallDisconnected, onSetCallActive, onSetCallInactive) przekazywane do CallsManager.addCall w celu obsługi działań inicjowanych przez te urządzenia.
Gdy wystąpi działanie zdalne, wywoływana jest odpowiednia funkcja lambda.
Pomyślne zakończenie działania funkcji Lambda oznacza, że polecenie zostało przetworzone. Jeśli polecenia nie można wykonać, funkcja lambda powinna zgłosić wyjątek.
Prawidłowe wdrożenie zapewnia płynne sterowanie połączeniami na różnych urządzeniach. Dokładnie przetestuj urządzenie na różnych powierzchniach.
Rozszerzenia połączeń
Oprócz zarządzania stanem połączenia i ścieżką dźwięku biblioteka obsługuje też rozszerzenia połączeń, czyli funkcje opcjonalne, które aplikacja może wdrożyć, aby zapewnić lepsze wrażenia z połączeń na platformach zdalnych, takich jak Android Auto. Obejmują one sale konferencyjne, wyciszanie połączeń i dodatkowe ikony połączeń. Gdy aplikacja zaimplementuje rozszerzenie, informacje, które udostępnia, będą synchronizowane ze wszystkimi połączonymi urządzeniami, które również obsługują wyświetlanie tych rozszerzeń w swoim interfejsie. Oznacza to, że te funkcje będą dostępne również na urządzeniach zdalnych, z którymi użytkownicy będą mogli wchodzić w interakcje.
Tworzenie połączenia z komponentami
Podczas tworzenia połączenia zamiast używać CallsManager.addCall, możesz użyć CallsManager.addCallWithExtensions, co daje aplikacji dostęp do innego zakresu o nazwie ExtensionInitializationScope. Ten zakres umożliwia aplikacji zainicjowanie zestawu opcjonalnych rozszerzeń, które obsługuje. Dodatkowo ten zakres udostępnia dodatkową metodę onCall, która po zakończeniu wymiany i inicjowania funkcji rozszerzenia CallControlScope umożliwia powrót do aplikacji.
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...
}
}
}
Uczestnicy połączenia z zespołem pomocy
Jeśli Twoja aplikacja obsługuje uczestników połączeń w przypadku spotkań lub połączeń grupowych, użyj
addParticipantExtension, aby zadeklarować obsługę tego rozszerzenia, i użyj powiązanych interfejsów API, aby aktualizować zdalne powierzchnie, gdy zmieniają się uczestnicy.
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)
}
}
Oprócz powiadamiania zdalnych urządzeń o tym, którzy uczestnicy są w rozmowie, można też aktualizować aktywnego uczestnika za pomocą ParticipantExtension#updateActiveParticipant.
Obsługiwane są też opcjonalne działania związane z uczestnikami połączenia.
Aplikacja może używać ParticipantExtension#addRaiseHandSupport, aby obsługiwać funkcję podnoszenia ręki przez uczestników rozmowy i sprawdzać, którzy inni uczestnicy również podnieśli rękę.
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)
}
}
Wyciszanie połączeń z zespołem pomocy
Funkcja wyciszania połączeń umożliwia użytkownikowi wyciszenie dźwięku wychodzącego połączenia w aplikacji bez fizycznego wyciszania mikrofonu urządzenia. Ta funkcja jest zarządzana dla każdego połączenia, więc Jetpack obsługuje złożoność zarządzania globalnym stanem wyciszenia trwających połączeń komórkowych, gdy aktywne jest połączenie VoIP. Dzięki temu wyciszanie dźwięku wychodzącego jest mniej podatne na błędy w przypadku wielu połączeń, a także umożliwia korzystanie z przydatnych funkcji, takich jak wskazanie „czy mówisz”, gdy użytkownik mówi, nie zdając sobie sprawy, że wyciszenie połączenia jest włączone.
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)
}
}
Ikony połączeń z zespołem pomocy
Ikona połączenia umożliwia aplikacji określenie niestandardowej ikony reprezentującej połączenie, która będzie wyświetlana na urządzeniach zdalnych podczas połączenia. Ikona ta może być też aktualizowana w trakcie połączenia.
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)
}
}
Dodawanie do systemowego dziennika połączeń
Możesz dodawać połączenia VoIP z aplikacji do systemowego rejestru połączeń, aby były widoczne w systemowej aplikacji do wybierania numerów i aby użytkownicy mogli z niej oddzwaniać. Więcej informacji znajdziesz w artykule Ujednolicona historia połączeń.