Telecom

Nowa biblioteka Android Telecom Jetpack ułatwia poinformowanie platformy, stan połączenia. Kod źródłowy i przykładową aplikację znajdziesz na stronie GitHub

Zależności i uprawnienia

Najpierw otwórz plik build.gradle modułu aplikacji i dodaj zależność Moduł androidx Telecom:

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

W manifeście aplikacji zadeklaruj, że aplikacja używa MANAGE_OWN_CALLS uprawnienie:

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

Zarejestruj aplikację

Aby powiadomić Androida o aplikacji, musisz ją zarejestrować i jej funkcje. Informuje on Androida, jakie funkcje obsługuje Twoja aplikacja, np. rozmowy wideo, połączenia strumieniowanie i zatrzymywanie połączeń. Te informacje są ważne, ponieważ Android może skonfigurować do obsługi funkcji aplikacji.

 private val callsManager = CallsManager(context)

var capabilities: @CallsManager.Companion.Capability Int =
    CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING

callsManager.registerAppWithTelecom(capabilities)

Integracja platformy

2 najczęstsze scenariusze połączeń w przypadku każdej aplikacji do wykonywania połączeń to połączenia przychodzące i połączenia wychodzące. Aby prawidłowo zarejestrować kierunek rozmowy i odpowiednio powiadamia użytkownika za pomocą powiadomień, skorzystaj z poniższych interfejsów API.

Zarejestruj rozmowę

Ten przykład pokazuje, jak zarejestrować połączenie przychodzące:

companion object {
  const val APP_SCHEME = "MyCustomScheme"
  const val ALL_CALL_CAPABILITIES = (CallAttributes.SUPPORTS_SET_INACTIVE
    or CallAttributes.SUPPORTS_STREAM or CallAttributes.SUPPORTS_TRANSFER)

  const val INCOMING_NAME = "Luke"
  val INCOMING_URI: Uri = Uri.fromParts(APP_SCHEME, "", "")
  // Define all possible properties for CallAttributes
  val INCOMING_CALL_ATTRIBUTES =
    CallAttributes(
      INCOMING_NAME,
      INCOMING_URI,
      DIRECTION_INCOMING,
      CALL_TYPE_VIDEO_CALL,
      ALL_CALL_CAPABILITIES)
}

Obiekt callAttributes może mieć te właściwości:

  • displayName: imię i nazwisko rozmówcy, spotkania lub sesji.
  • address: adres połączenia. Uwaga: tę opcję można wydłużyć do spotkania .
  • direction: kierunek połączenia, na przykład przychodzące lub wychodzące.
  • callType: informacje związane z przesyłanymi danymi, np. wideo. i dźwięk.
  • callCapabilities: obiekt określający możliwości wywołania.

Obiekt callCapabilities może mieć te właściwości:

  • streaming: wskazuje, czy rozmowa obsługuje strumieniowanie dźwięku na inne urządzenie Urządzenie z systemem Android.
  • transfer: wskazuje, czy połączenie można przekazać.
  • hold: wskazuje, czy połączenie można zawiesić.

Dodaj połączenie

Metoda addCall() zwraca wyjątek, jeśli urządzenie nie obsługuje tej funkcji telekomunikacji lub jeśli podczas konfigurowania połączenia wystąpił błąd.

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onIsCallAnswered, // Watch needs to know if it can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
        callControlScope = this
    }
}

Odbieranie połączenia

Po nawiązaniu połączenia przychodzącego musisz je odebrać lub odrzucić. Ten przykład pokazuje, jak odebrać połączenie:

when (answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {

    }
}

Jeśli trwa inne połączenie, answer() wróci CallControlResult.Error informujący, dlaczego nie udało się odebrać połączenia. W w takim przypadku użytkownik musi zawiesić drugie połączenie.

Odrzucanie połączenia

Aby odrzucić połączenie, rozłącz się z firmą DisconnectCause.Rejected.

fun onRejectCall(){
    coroutineScope.launch {
        callControlScope?.let {
            it.disconnect(DisconnectCause(DisconnectCause.REJECTED))
        }
    }
}

Połączenie wychodzące

Podczas nawiązywania połączenia wychodzącego, gdy osoba zdalna odbierze połączenie, należy ustawić active to sygnał, aby poinformować platformę, że wywołanie jest w toku:

when (setActive()) {
    is CallControlResult.Success -> {
        onIsCallActive()
    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

Przełączanie połączenia w stan oczekiwania

Jeśli aplikacja do rozmów obsługuje wstrzymywanie połączeń, użyj funkcji setInActive, aby przekazać który nie jest aktywny, a mikrofon i kamera mogą mogą być wykorzystywane przez inne aplikacje:

when (setInActive()) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

Odłącz

Aby zakończyć połączenie, przekaż stosowi telekomunikacyjnemu prośbę o rozłączenie, podłączając właściwa przyczyna:

coroutineScope.launch {
    callControlScope?.disconnect(DisconnectCause(DisconnectCause.LOCAL))
}

Kieruj dźwięk

Podczas połączenia użytkownicy czasami przełączają się między urządzeniami, takimi jak głośnik, słuchawką lub urządzeniem Bluetooth. Korzystaj z dokumentów availableEndpoints oraz interfejsów API usługi currentCallEndpoint, aby uzyskać listę wszystkich urządzeń dostępnych dla użytkownika i urządzenie, które jest aktywne.

Ten przykład łączy oba procesy, tworząc obiekt UI, który pokazuje użytkownikowi listę urządzeń oraz informacje o tym, które z nich jest aktywne:

availableEndpoint = combine(callControlScope.availableEndpoints,
    callControlScope.currentCallEndpoint) {
    availableDevices: List<CallEndpoint>, activeDevice : CallEndpoint ->
    availableDevices.map {
        EndPointUI(
            isActive = activeDevice.endpointName == it.endpointName, it
        )
    }
}

Aby zmienić aktywne urządzenie, użyj requestEndpointChange z CallEndpoint, na które chcesz wprowadzić zmiany.

coroutineScope.launch {
     callControlScope?.requestEndpointChange(callEndpoint)
}

Obsługa na pierwszym planie

Biblioteka Telecom obejmuje obsługę na pierwszym planie. Ta biblioteka używa ConnectionService na urządzeniach z Androidem 13 lub starszym. Android 14 i wyżej używa mikrofonu i aparatu typu pierwszego planu, i obsługują usługi na pierwszym planie. Więcej informacji o usługach na pierwszym planie

W ramach wymagań dotyczących pierwszego planu aplikacja musi opublikować powiadomienie aby użytkownicy wiedzieli, że aplikacja działa na pierwszym planie.

Aby mieć pewność, że aplikacja będzie miała priorytet wykonywania na pierwszym planie, utwórz powiadomienia po zarejestrowaniu połączenia na platformie. Priorytet na pierwszym planie zostanie usunięte, gdy aplikacja zakończy połączenie lub powiadomienie nie będzie już prawidłowe.

is TelecomCall.Registered -> {
    val notification = createNotification(call)
    notificationManager.notify(TELECOM_NOTIFICATION_ID, notification)
}

Obsługa powierzchni

Zegarki mają ogólną aplikację odbiornika punktu końcowego. Ta aplikacja zapewnia Użytkownik za pomocą podstawowego interfejsu do odbierania, odrzucania i rozłączania połączeń. Aplikacja obsługuje te działania dzięki wdrożeniu funkcji lambda które informują platformę, o której wykonano działanie na urządzeniu.

Każda funkcja lambda przekracza limit czasu po 5 sekundach z nieudaną transakcją, jeśli aplikacja nie odpowiada.

callsManager.addCall(
        attributes,
        onIsCallAnswered, // Watch/Auto need to know if they can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
//Call Scope
}
/**
  *  Can the call be successfully answered??
  *  TIP: Check the connection/call state to see if you can answer a call
  *  Example you may need to wait for another call to hold.
  **/
val onIsCallAnswered: suspend(type: Int) -> Unit = {}

/**
  * Can the call perform a disconnect
  */
val onIsCallDisconnected: suspend (cause: DisconnectCause) -> Unit = {}

/**
  *  Check is see if you can make the call active.
  *  Other calls and state might stop us from activating the call
  */
val onIsCallActive: suspend () -> Unit = {
    updateCurrentCall {
    }
}

/**
  * Check to see if you can make the call inactivate
  */
val onIsCallInactive: suspend () -> Unit = {}