PhoneCall

전화 수/발신 기능 제어를 위한 규격

Version

최신 버전은 1.3 입니다.

Version Date Description
1.0 2020.07.06 규격 추가
1.1 2020.08.28 SendCandidates directive 에 clientSearchTargetList 추가
1.2 2020.11.02 Context 에 searchScene 추가
SendCandidates directive 에 searchScene 추가
SendCandidates directive 의 clientSearchTargetList 삭제
Context 에 recipient 추가
MakeCallSucceeded event 추가
1.3 2022.02.15 Context,SendCandidates intent에 “SAVE_CONTACT”, “BLOCK”, “EXACT_ONE” 추가
Contact Object 에 isBlocked 추가
Person Object에 poiId 추가
BlockNumber 디렉티브 추가

State Diagram

SDK Interface

PhoneCallAgent 사용

PhoneCall interface 규격에 따른 디바이스의 동작 제어는 PhoneCallAgent 가 처리합니다.

  • Android
  • iOS
  • Linux

NuguAndroidClient 생성시 PhoneCallAgent 를 추가합니다.

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyPhoneCallClient: PhoneCallClient {
    ...
}
NuguAndroidClient().Builder()
            .addAgentFactory(PhoneCallAgent.NAMESPACE, object : AgentFactory<PhoneCallAgent> {
                override fun create(container: SdkContainer): PhoneCallAgent = with(container) {
                    PhoneCallAgent(
                        MyPhoneCallClient(),
                        getContextManager(),
                        getMessageSender(),
                        getAudioSeamlessFocusManager(),
                        DefaultFocusChannel.COMMUNICATIONS_CHANNEL_NAME,
                        getDirectiveSequencer(),
                        getInteractionControlManager()
                    )
                }
            })

NuguAndroidClient instance 를 통해 PhoneCallAgent instance 에 접근할 수 있습니다.

복사성공!
1
val phoneCallAgent = nuguAndroidClient.getAgent(PhoneCallAgent.NAMESPACE)

NuguClient instance 를 통해 PhoneCallAgent instance 에 접근할 수 있습니다.

복사성공!
1
let phoneCallAgent = nuguClient.phoneCallAgent

CapabilityFactory::makeCapability 함수로 PhoneCallAgent 를 생성하고 NuguClient 에 추가해 주어야합니다.

복사성공!
1
2
3
4
5
6
auto phonecall_handler(std::shared_ptr<IPhoneCallHandler>(
        CapabilityFactory::makeCapability<PhoneCallAgent, IPhoneCallHandler>()));

nugu_client->getCapabilityBuilder()
    ->add(phonecall_handler.get())
    ->construct();

Context 구성

통화 상태 정보를 Context 에 포함시켜 주어야 합니다.

  • Android
  • iOS

PhoneCallClient 를 구현합니다.

복사성공!
1
2
3
4
5
6
7
class MyPhoneCallClient: PhoneCallClient {
    override fun getContext(): Context {
        ...
    }

    ...
}

PhoneCallAgentDelegate 를 추가합니다.

복사성공!
1
2
3
4
5
6
7
8
class MyPhoneCallAgentDelegate: PhoneCallAgentDelegate {
    func phoneCallAgentRequestContext() -> PhoneCallContext {
        ...
    }

    ...
}
phoneCallAgent.delegate = self

발신

전화 발신이 SendCandidates, MakeCall directive 로 요청될 수 있습니다.

  • Android
  • iOS
  • Linux

PhoneCallClient 를 구현합니다.

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
class MyPhoneCallClient: PhoneCallClient {
    override fun sendCandidates(payload: SendCandidatesPayload, callback: Callback) {
        // 연락처 검색 기능을 구현
        ...
    }

    override fun makeCall(payload: MakeCallPayload, callback: Callback) {
        // 전화 발신 기능을 구현
        ...
    }
    ...
}

PhoneCallAgentDelegate 를 추가합니다

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyPhoneCallAgentDelegate: PhoneCallAgentDelegate {
    func phoneCallAgentDidReceiveSendCandidates(item: PhoneCallCandidatesItem, dialogRequestId: String) {
        // 연락처 검색 기능을 구현
        ...
    }

    func phoneCallAgentDidReceiveMakeCall(callType: PhoneCallType, recipient: PhoneCallPerson, dialogRequestId: String) -> PhoneCallErrorCode? {
        // 전화 발신 기능을 구현
        ...
    }

    ...
}
phoneCallAgent.delegate = self

IPhoneCallListener 를 추가합니다.

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyPhoneCallListener : public IPhoneCallListener {
public:
    ...

    void processSendCandidates(const std::string& payload) override
    {
        ...
    }
    
    void processMakeCall(const std::string& payload) override
    {
        ...
    }

    ...
};
auto phonecall_listener(std::make_shared<MyPhoneCallListener>());
CapabilityFactory::makeCapability<PhoneCallAgent, IPhoneCallHandler>(phonecall_listener.get());

수신

수신 전화에 대한 수락이 AcceptCall directive 로 거절이 EndCall directive 로 요청될 수 있습니다.

iOS 는 수신 수락/거절 기능을 제공하지 않습니다.

  • Android
  • Linux

PhoneCallClient 를 구현합니다.

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
class MyPhoneCallClient: PhoneCallClient {
    override fun acceptCall(payload: AcceptCallPayload) {
        // 수신 수락 기능을 구현
        ...
    }

    override fun makeCall(payload: MakeCallPayload, callback: Callback) {
        // 수신 거절 기능을 구현
        ...
    }
    ...
}

IPhoneCallListener 를 추가합니다.

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
class MyPhoneCallListener : public IPhoneCallListener {
public:
    ...

    void processAcceptCall(const std::string& payload)
    {
        ...
    }
    
    ...
};
auto phonecall_listener(std::make_shared<MyPhoneCallListener>());
CapabilityFactory::makeCapability<PhoneCallAgent, IPhoneCallHandler>(phonecall_listener.get());

Context

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
  "PhoneCall": {
    "version": "1.1",
    "state": "{{STRING}}",
    "template": {
      "intent": "{{STRING}}",
      "callType": "{{STRING}}",
      "recipientIntended": {
        "name": "{{STRING}}",
        "label": "{{STRING}}"
      },
      "searchScene": "{{STRING}}",
      "candidates": [Person]
    },
    "recipient": {
        "name": "{{STRING}}",
        "token": "{{STRING}}",
        "isMobile": "{{STRING}}",
        "isRecentMissed": "{{STRING}}"
    },
    "numberBlockable": "{{STRING}}"
  }
}
parameter type mandatory description
state string Y IDLE
OUTGOING
INCOMING
ESTABLISHED
template object N Play 에서 진행중인 intent 를 알 수 있도록 SendCandidates event 에서 전달받은 data 를 유지해야 함
template.intent string N candidates의 용도
- CALL : 전화걸어줘
- SEARCH : 찾아줘
- HISTORY : 최근 통화 목록
- REDIAL : 재발신
- MISSED : 부재중통화
- EXACT_ONE : 전화추천 (전달된 candidates에 새로운 수신자 후보를 추가하지 않고 동명이 있을 경우 하나만 전달)
- SAVE_CONTACT
- BLOCK
- NONE
template.callType string N NORMAL : 일반 전화
SPEAKER : 스피커폰
VIDEO : 비디오콜
CALLAR : 콜라
template.recipientIntended object N 발화에서 분석된 recipient 정보
template.recipientIntended.name string N 검색에 요청할때 사용된 상대방 이름 (NLU 분석으로 나온 이름)
template.label string N NLU 분석 결과 label (집, 회사, …) 정규화되어 있지 않고, 사용자 발화에서 분석된 값을 그대로 보냄
template.searchScene string N SendCandidates 참조
template.candidates array of Person N 화면에 검색 결과 리스트를 디스플레이하는 중에만 context에 추가
recipient object N 통화 상대방에 대한 정보 수신중(INCOMING) 상태에서는 발신자 정보를 세팅 (CallArrived Event의 caller 정보)
recipient.name string N 통화 상대방의 이름
recipient.token string N 통화 상대방을 식별하기 위한 unique 값
recipient.isMobile string N 통화 상대방 전화번호가 모바일 폰인지 여부 (TRUE/FALSE)
recipient.isRecentMissed string N 통화 상대방과의 가장 최근 통화가 부재중 통화인지 여부
- TRUE : 수신 중인 전화번호의 가장 최근 수신 이력이 있지만 못받은 경우
- FALSE : 수신 중인 전화번호의 수신 이력이 없거나, 있는데 부재중 통화가 아닌 경우
numberBlockable string N 수신차단 전화번호 추가 가능 여부 (수신차단 번호가 1,000개 초과하면 false)TRUE, FALSE(default)

Common Objects

Person

  • 하나의 연락처를 나타내는 포맷
복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "name": "{{STRING}}",
  "type": "{{STRING}}",
  "profileImgUrl": "{{STRING}}",
  "category": "{{STRING}}",
  "address": {
    "road": "{{STRING}}",
    "jibun": "{{STRING}}"
  },
  "businessHours": {
    "open": "{{STRING}}",
    "close": "{{STRING}}",
    "info": "{{STRING}}"
  },
  "history": {
    "time": "{{STRING}}",
    "type": "{{STRING}}",
    "callType": "{{STRING}}"
  },
  "numInCallHistory": "{{STRING}}",
  "token": "{{STRING}}",
  "score": "{{STRING}}",
  "contacts": [Contact],
  "poiId": "{{STRING}}"
}
parameter type mandatory description
name string Y candidates가 존재하면 각 candidate는 최소한 이름은 포함해야 함
type string Y recipient candidates의 타입
- CONTACT : 연락처 검색
- EXCHANGE : exchange 검색
- T114 : T114 검색
- NONE : 특정 카테고리에 속하지 않음
profileImgUrl string N profile image URL
category string N 업종
address object N 주소
address.road string N 도로명 주소
address.jibun string N 지번 주소
businessHours object N 영업시간
businessHours.open string N 날짜, 시간 정보로 ISO 8601 포맷 (2007-12-03T10:15:30) HH:mm
businessHours.close string N 날짜, 시간 정보로 ISO 8601 포맷 (2007-12-03T10:15:30) HH:mm
businessHours.info string N 부가정보
history object N 통화 기록을 위한 정보
history.time string N 날짜, 시간 정보로 ISO 8601 포맷 (2007-12-03T10:15:30)
history.type string N
  • OUT : 발신 통화 (Outgoing call)
    - OUT_CANCELED : 발신 연결 안 됨 (Outgoing call canceled)
    - IN : 수신 통화 (Incoming call)
    - REJECTED : 수신 거절 (Rejected call)
    - MISSED : 부재중 통화 (Missed call)
    - VOICE_MESSAGE : 음성 메시지 (Voice message)
    - BLOCKED : 수신 차단 (Blocked)
history.callType string N
  • NORMAL : 일반통화
    - VIDEO : 영상통화
    - CALLAR : 콜라 영상통화
    - GROUP : 그룹통화
    - VOICE_MESSAGE : 음성 메시지 (Voice message)
numInCallHistory string N 발신기록 히스토리에 존재하는 건수 0, 1, 2, … 값을 string 형태로 받음
token string N 사용자 추가 정보를 식별하기 위해 임의로 정의한 key값을 포함 unique 여부는 사용되는 용도에 의해 결정
score string N 검색 결과의 신뢰도
contacts array of Contact N -
poiId string N 상호명 발화시 tmap등을 연결하기 위한 poi_id

Contact

  • 하나의 연락처를 나타내는 포맷
복사성공!
1
2
3
4
5
{
  "label": "{{STRING}}",
  "number": "{{STRING}}",
  "isBlocked": "{{STRING}}"
}
parameter type mandatory description
label string N MOBILE
COMPANY
HOME
USER_DEFINED : 사용자가 지정한 값도 필요할지 검토 필요
number string N 전화번호
isBlocked string N 수신 차단 여부
TRUE, FALSE(default)

Directives

SendCandidates

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
  "header": {
    "namespace": "PhoneCall",
    "name": "SendCandidates",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.1"
  },
  "payload": {
    "playServiceId": "{{STRING}}",
    "intent": "{{STRING}}",
    "recipientIntended": {
      "name": "{{STRING}}",
      "label": "{{STRING}}"
    },
    "callType": "{{STRING}}",
    "searchScene": "{{STRING}}",
    "candidates": [array of Person]
  }
}
parameter type mandatory description
intent string Y CALL : 전화걸어줘
SEARCH : 찾아줘
HISTORY : 최근 통화 목록
REDIAL : 재발신
MISSED : 부재중통화
NONE
recipientIntended object N 발화에서 분석된 recipient 정보
name string N 검색에 요청할때 사용된 상대방 이름 (NLU 분석으로 나온 이름)
label string N NLU 분석 결과 label (집, 회사, …) 정규화되어 있지 않고, 사용자 발화에서 분석된 값을 그대로 보냄
callType string N NORMAL : 일반 전화
SPEAKER : 스피커폰
VIDEO : 비디오콜
CALLAR : 콜라
searchScene string N 검색 대상과 화면을 정의하기 위해 추가 enum은 아니며 임의의 string이 올 수 있음 → 확장 가능성이 높아서 string으로 정의 (아래에 추가되는 값들은 여러 poc에서 공유해서 사용하는 값으로 추가 시 아래의 리스트를 계속 확장 가능
- DEFAULT : 기본 검색 로직
- T114ONLY : T114 검색결과만 노출되는 검색 로직
- T114INCLUDE : T114 검색결과를 항상 포함하는 검색 로직
- T114DIRECT : 긴급전화
candidates array of Person N candidates가 없으면 이 항목이 없어야 함

MakeCall

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "header": {
    "namespace": "PhoneCall",
    "name": "MakeCall",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}",
    "recipient": Person,
    "callType": "{{STRING}}"
  }
}
parameter type mandatory description
recipient Person Y -
callType string Y NORMAL : 일반 전화
SPEAKER : 스피커폰
VIDEO : 비디오콜
CALLAR : 콜라

EndCall

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
{
  "header": {
    "namespace": "PhoneCall",
    "name": "EndCall",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}"
  }
}

AcceptCall

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "header": {
    "namespace": "PhoneCall",
    "name": "AcceptCall",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}",
    "speakerPhone": {{BOOLEAN}}"
  }
}
parameter type mandatory description
speakerPhone bool Y 스피커폰으로 받을지 여부

BlockIncomingCall

  • 현재 수신 중인 전화 수신 차단 설정
복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
{
  "header": {
    "namespace": "PhoneCall",
    "name": "BlockIncomingCall",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}"
  }
}

BlockNumber

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "header": {
    "namespace": "PhoneCall",
    "name": "BlockNumber",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}",
    "number": "{{STRING}}",
    "blockType": "{{STRING}}"
  }
}
parameter type mandatory description
number string Y 전화번호
blockType string Y EXACT
PREFIX
POSTFIX

Events

CandidatesListed

  • 검색 결과 리스트가 화면에 보여지는 경우 보내는 Event
  • Parameter 들은 Context 를 통해 전송되며, 검색 결과가 없는 경우에도 empty 로 전송해야 함.
복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
{
  "header": {
    "namespace": "PhoneCall",
    "name": "CandidatesListed",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}"
  }
}

CallArrived

복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "header": {
    "namespace": "PhoneCall",
    "name": "CallArrived",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}",
    "caller": {
      "name": "{{STRING}}",
      "token": "{{STRING}}",
      "isMobile": "{{STRING}}",
      "isRecentMisssed": "{{STRING}}"
    }
  }
}
parameter type mandatory description
caller object Y 발신자 정보
caller.name string N 이름
caller.token string Y 전화를 건 상대방인 식별하기 위한 unique 값
CallArrived Event 후에 응답을 보낼때 해당 Event를 발생시킨 caller를 unique하게 구분하기 위해 SDK에서 생성하는 token 값
caller.isMobile string Y 모바일 폰인지 여부(TRUE/FALSE)
caller.isRecentMissed string Y 가장 최근 통화가 부재중 통화인지 여부
- TRUE : 수신 중인 전화번호의 가장 최근 수신 이력이 있지만 못받은 경우
- FALSE : 수신 중인 전화번호의 수신 이력이 없거나, 있는데 부재중 통화가 아닌 경우

CallEnded

  • EndCall로 명시적으로 전화를 끊은 경우
  • ONGOING인 경우는 상대방이 거절하거나 통화중, 전화를 안받는 경우 등 연결이 안되는 모든 상태
복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
{
  "header": {
    "namespace": "PhoneCall",
    "name": "CallEnded",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}"
  }
}

CallEstablished

  • 상대방이 전화를 받은 경우
  • AcceptCall이 성공해서 연결된 경우
복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
{
  "header": {
    "namespace": "PhoneCall",
    "name": "CallEstablished",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}"
  }
}

MakeCallFailed

  • MakeCall이 실패하는 경우 → 기능 상의 이슈로 전화 연결 시도 자체가 실패하는 경우
복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "header": {
    "namespace": "PhoneCall",
    "name": "MakeCallFailed",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.0"
  },
  "payload": {
    "playServiceId": "{{STRING}}",
    "errorCode": "{{STRING}}",
    "callType": "{{STRING}}"
  }
}
parameter type mandatory description
errorCode string Y NO_SYSTEM_PERMISSION : 권한없음
CALL_TYPE_NOT_SUPPORTED : 해당 callType 을 지원하지 않음
callType string Y NORMAL : 일반 전화
SPEAKER : 스피커폰
VIDEO : 비디오콜
CALLAR : 콜라

MakeCallSucceeded

  • MakeCall이 성공하는 경우
복사성공!
1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "header": {
    "namespace": "PhoneCall",
    "name": "MakeCallSucceeded",
    "messageId": "{{STRING}}",
    "dialogRequestId": "{{STRING}}",
    "version": "1.2"
  },
  "payload": {
    "playServiceId": "{{STRING}}",
    "recipient": Person
  }
}
parameter type mandatory description
recipient Person Y