통신 회사

새로운 Android Telecom Jetpack 라이브러리를 사용하면 통화 상태를 플랫폼에 쉽게 알릴 수 있습니다. GitHub에서 소스 코드와 샘플 앱을 찾을 수 있습니다.

종속 항목 및 권한

먼저 앱 모듈 build.gradle 파일을 열고 androidx Telecom 모듈의 종속 항목을 추가합니다.

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

앱 매니페스트에서 앱이 MANAGE_OWN_CALLS 권한을 사용한다고 선언합니다.

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

애플리케이션 등록

Android에 앱에 관해 알리려면 앱과 앱의 기능을 등록해야 합니다. 이를 통해 영상 통화, 통화 스트리밍, 통화 대기 등 앱이 지원하는 기능이 Android에 무엇인지 알 수 있습니다. 이 정보는 Android에서 앱의 기능을 사용하도록 자체적으로 구성하는 데 중요합니다.

 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)

플랫폼 통합

통화 애플리케이션에서 가장 일반적인 두 가지 통화 시나리오는 수신 전화와 발신 전화입니다. 호출 방향을 올바르게 등록하고 사용자에게 알림을 적절하게 알리려면 아래 API를 사용하세요.

통화 등록

이 예에서는 수신 전화를 등록하는 방법을 보여줍니다.

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)
}

callAttributes 객체에는 다음 속성이 포함될 수 있습니다.

  • displayName: 발신자, 회의 또는 세션의 이름입니다.
  • address: 호출의 주소입니다. 이는 회의 링크로 확장할 수 있습니다.
  • direction: 수신 또는 발신과 같은 통화의 방향입니다.
  • callType: 동영상, 오디오 등 전송 중인 데이터와 관련된 정보입니다.
  • callCapabilities: 호출 기능을 지정하는 객체입니다.

callCapabilities 객체에는 다음 속성이 포함될 수 있습니다.

  • streaming: 통화가 다른 Android 지원 기기로 오디오 스트리밍을 지원하는지 여부를 나타냅니다.
  • transfer: 통화를 전달할 수 있는지를 나타냅니다.
  • hold: 통화를 대기할 수 있는지 여부를 나타냅니다.

통화 추가

기기가 텔레콤을 지원하지 않거나 통화를 설정할 때 오류가 발생하면 addCall() 메서드는 예외를 반환합니다.

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

전화 받기

수신 전화를 걸면 전화를 받거나 거부해야 합니다. 이 시험은 전화를 받는 방법을 보여줍니다.

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

    }

    is CallControlResult.Error -> {

    }
}

다른 통화가 진행 중이면 answer()CallControlResult.Error를 반환하여 통화에 응답할 수 없는 이유를 알려줍니다. 이 경우 사용자는 다른 통화를 대기 상태로 전환해야 합니다.

통화 거부

전화를 거부하려면 DisconnectCause.Rejected로 통화를 끊으세요.

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

발신 전화

발신 전화를 걸 때 원격 당사자가 전화를 받으면 호출을 active로 설정하여 플랫폼이 통화가 진행 중임을 인식하도록 해야 합니다.

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

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

통화를 대기 상태로 전환

통화 앱에서 통화 대기를 지원하는 경우 setInActive를 사용하여 통화가 활성화되지 않았으며 다른 앱에서 마이크와 카메라를 자유롭게 사용할 수 있다고 플랫폼에 알립니다.

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

    }

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

연결 해제

통화 연결을 끊으려면 유효한 원인을 제공하여 텔레콤 스택에 연결을 해제하도록 알립니다.

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

오디오 라우팅

통화 중에 사용자가 스피커, 스피커, 블루투스 기기와 같은 기기 간에 전환하는 경우가 있습니다. availableEndpointscurrentCallEndpoint API를 사용하여 사용자가 사용할 수 있는 모든 기기 목록과 활성 기기를 가져옵니다.

이 예에서는 두 흐름을 결합하여 사용자에게 기기 목록과 활성 상태의 기기를 표시할 UI 객체를 만듭니다.

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

활성 기기를 변경하려면 변경하려는 CallEndpoint와 함께 requestEndpointChange를 사용합니다.

coroutineScope.launch {
     callControlScope?.requestEndpointChange(callEndpoint)
}
을 사용하도록 미디어 스트림을 구성해야 합니다.

포그라운드 지원

텔레콤 라이브러리는 포그라운드 지원이 제공됩니다. 이 라이브러리는 Android 13 및 이전 버전을 실행하는 기기에 ConnectionService를 사용합니다. Android 14 이상에서는 foregroundtypes 마이크와 카메라를 사용하여 포그라운드 서비스를 올바르게 지원합니다. 포그라운드 서비스 자세히 알아보기

포그라운드 요구사항의 일부로 애플리케이션은 사용자에게 애플리케이션이 포그라운드에서 실행 중임을 알 수 있도록 알림을 게시해야 합니다.

앱이 포그라운드 실행 우선순위를 받도록 하려면 플랫폼에 호출을 등록한 후 알림을 만듭니다. 앱이 통화를 종료하거나 알림이 더 이상 유효하지 않으면 포그라운드 우선순위가 삭제됩니다.

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

표면 지원

시계에 일반 엔드포인트 수신기 애플리케이션이 있습니다. 이 애플리케이션은 사용자에게 전화 받기, 거부, 연결 해제와 같은 기본 인터페이스를 제공합니다. 애플리케이션은 기기에서 작업을 실행했음을 플랫폼에 알리는 람다 함수를 구현하여 이러한 작업을 지원합니다.

애플리케이션이 응답하지 않으면 각 람다 함수는 5초 후에 타임아웃되며 트랜잭션이 실패합니다.

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 = {}