Biblioteka Core-Telecom
upraszcza proces integracji aplikacji wywołującej z platformą Android, zapewniając niezawodny i spójny zestaw interfejsów API.
Jeśli chcesz zapoznać się z praktycznymi implementacjami, znajdziesz przykładowe aplikacje na GitHubie:
- Lekka przykładowa aplikacja – minimalny przykład wykorzystania interfejsu API
Core-Telecom
. Idealne do szybkiego zrozumienia podstawowych pojęć. - Kompleksowa przykładowa aplikacja (opracowana przez zespół Core-Telecom) – aplikacja z większą liczbą funkcji, która demonstruje zaawansowane funkcje telekomunikacyjne i sprawdzone metody. To świetne źródło informacji o skomplikowanych scenariuszach integracji.
Konfigurowanie usługi Core-Telecom
Dodaj zależność androidx.core:core-telecom
do pliku build.gradle
aplikacji:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
Deklaruj uprawnienie MANAGE_OWN_CALLS
w pliku AndroidManifest.xml
:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
Rejestracja swojej aplikacji
Aby zacząć dodawać połączenia do systemu, zarejestruj aplikację do połączeń w Androidzie, używając CallsManager
. Podczas rejestracji określ funkcje aplikacji (np. obsługę dźwięku lub 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 i zarządzania cyklem życia połączeń.
Tworzenie połączenia
Obiekt CallAttributesCompat
definiuje właściwości unikalnego połączenia, które może mieć te cechy:
displayName
: nazwa dzwoniącego.address
: adres wywołania (np. numer telefonu, link do spotkania).direction
: przychodzące lub wychodzące.callType
: dźwięk lub obraz.callCapabilities
: obsługuje przenoszenie i blokowanie.
Oto przykład tworzenia połączenia przychodzącego:
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
)
}
Dodawanie połączenia
Użyj funkcji callsManager.addCall
z opcją CallAttributesCompat
i zgłoszeniami zwrotnymi, aby dodać nowe wywołanie do systemu i zarządzać zdalnym aktualizowaniem powierzchni. W bloku addCall
funkcja callControlScope
umożliwia aplikacji przejście do 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ą aplikacji disconnect()
z użyciem aplikacji DisconnectCause.REJECTED
w aplikacji CallControlScope
:
disconnect(DisconnectCause(DisconnectCause.REJECTED))
Aktywowanie połączenia wychodzącego
Aby ustawić połączenie wychodzące jako aktywne, gdy osoba po drugiej stronie odbierze:
when (val result = setActive()) {
is CallControlResult.Success -> { /* Call active */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Przełączanie połączenia w stan oczekiwania
Aby wstrzymać połączenie, naciśnij setInactive()
:
when (val result = setInactive()) {
is CallControlResult.Success -> { /* Call on hold */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Rozłączanie połączenia
Rozłączanie połączenia za pomocą disconnect()
z urządzeniem DisconnectCause
:
disconnect(DisconnectCause(DisconnectCause.LOCAL))
Zarządzanie punktami końcowymi dźwięku w połączeniu
Obserwowanie punktów końcowych dźwięku i zarządzanie nimi za pomocą currentCallEndpoint
, availableEndpoints
i isMuted
Flow
w CallControlScope
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, używając requestEndpointChange()
:
coroutineScope.launch {
callControlScope.requestEndpointChange(callEndpoint)
}
Pomoc na pierwszym planie
Biblioteka używa ConnectionService
(Android 13 API na poziomie 33 lub niższym) lub foregroundtypes (Android 14 API na poziomie 34 lub wyższym) do obsługi pierwszego planu.
Zgodnie z wymaganiami dotyczącymi działania na pierwszym planie aplikacja musi wyświetlać powiadomienie, aby użytkownicy wiedzieli, że działa ona na pierwszym planie.
Aby mieć pewność, że Twoja aplikacja będzie miała priorytet w wykonywaniu na pierwszym planie, utwórz powiadomienie po dodaniu wywołania w ramach platformy. Priorytet na pierwszym planie jest usuwany, gdy aplikacja zakończy połączenie lub gdy powiadomienie nie jest już ważne.
Więcej informacji o usługach na pierwszym planie
Pomoc zdalna dla urządzeń Surface
Urządzenia zdalne (smartwatche, słuchawki Bluetooth, Android Auto) mogą zarządzać połączeniami bez bezpośredniej interakcji z telefonem. Aplikacja musi implementować wywołania zwrotne lambda (onAnswerCall
, onSetCallDisconnected
, onSetCallActive
,
onSetCallInactive
) udostępniane do usługi CallsManager.addCall
w celu obsługi działań inicjowanych przez te urządzenia.
Gdy wystąpi działanie zdalne, zostanie wywołana odpowiednia funkcja lambda.
Pomyślne zakończenie działania funkcji lambda sygnalizuje, że polecenie zostało przetworzone. Jeśli polecenie nie może zostać wykonane, funkcja lambda powinna wyrzucić wyjątek.
Właściwe wdrożenie zapewnia płynne sterowanie rozmowami na różnych urządzeniach. Dokładnie przetestuj na różnych powierzchniach.
Rozszerzenia połączeń
Oprócz zarządzania stanem połączeń i trasą audio biblioteka obsługuje też rozszerzenia połączeń, czyli opcjonalne funkcje, które aplikacja może wdrażać, aby zapewnić lepsze wrażenia z rozmów na urządzeniach zdalnych, takich jak Android Auto. Funkcje te obejmują sale konferencyjne, wyciszenie rozmów i dodatkowe ikony rozmów. Gdy aplikacja wdroży rozszerzenie, informacje, które udostępnia, zostaną zsynchronizowane ze wszystkimi połączonymi urządzeniami, które obsługują wyświetlanie tych rozszerzeń w interfejsie. Oznacza to, że te funkcje będą też dostępne na urządzeniach zdalnych, z których użytkownicy będą mogli korzystać.
Tworzenie połączenia z rozszerzeniami
Podczas tworzenia połączenia zamiast używać funkcji CallManager#addCall
możesz użyć funkcji CallManager#addCallWithExtensions, która 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 zawiera dodatkową metodę onCall
, która zapewnia CallControlScope
z powrotem do aplikacji po zakończeniu wymiany i inicjalizacji rozszerzenia.
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 rozmowy z pomocą
Jeśli Twoja aplikacja obsługuje uczestników połączeń na spotkaniach lub w przypadku połączeń grupowych, użyj interfejsu addParticipantExtension
, aby zadeklarować obsługę tego rozszerzenia, oraz odpowiednich interfejsów API, aby aktualizować zdalne interfejsy, 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 informowania urządzeń zdalnych o tym, którzy uczestnicy są w połączeniu, aktywny uczestnik może też otrzymywać informacje za pomocą funkcji ParticipantExtension#updateActiveParticipant
.
Dostępne są też opcjonalne działania dotyczące uczestników rozmowy.
Aplikacja może używać ParticipantExtension#addRaiseHandSupport
do obsługi funkcji podnoszenia ręki przez uczestników spotkania i do wyświetlania informacji o tym, którzy inni uczestnicy również podnieśli ręce.
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)
}
}
Wyciszenie podczas rozmowy z zespołem pomocy
Funkcja wyciszania rozmów umożliwia użytkownikowi wysłanie prośby do aplikacji o wyciszenie dźwięku podczas połączeń wychodzących bez fizycznego wyciszenia mikrofonu urządzenia. Ta funkcja jest zarządzana w poszczególnych połączeniach, 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 wyciszenie dźwięku podczas połączeń wychodzących jest mniej podatne na błędy w sytuacjach z wieloma połączeniami, a także umożliwia korzystanie z przydatnych funkcji, takich jak sygnał „Czy mówisz?”, gdy użytkownik mówi, nie zdając sobie sprawy, że wyciszenie 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 zdalnych powierzchniach podczas połączenia. Ta ikona może się też zmieniać w trakcie trwania 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)
}
}