in source/android/mobile/src/main/java/io/adaptivecards/adaptivecardssample/RemoteClientConnection.java [147:360]
private void connectToHost(final JSONObject offerAndCandidates)
{
m_observer.onConnecting("Accepting offer...");
// Code from https://youtu.be/HS1eKPL4f1o
PeerConnectionFactory.InitializationOptions options = PeerConnectionFactory.InitializationOptions.builder(m_context).createInitializationOptions();
PeerConnectionFactory.initialize(options);
PeerConnectionFactory factory = PeerConnectionFactory.builder().createPeerConnectionFactory();
ArrayList<PeerConnection.IceServer> iceServers = new ArrayList<>();
iceServers.add(PeerConnection.IceServer.builder("stun:ws-turn3.xirsys.com").createIceServer());
String[] turnServers = new String[]
{
"turn:ws-turn3.xirsys.com:80?transport=udp",
"turn:ws-turn3.xirsys.com:3478?transport=udp",
"turn:ws-turn3.xirsys.com:80?transport=tcp",
"turn:ws-turn3.xirsys.com:3478?transport=tcp",
"turns:ws-turn3.xirsys.com:443?transport=tcp",
"turns:ws-turn3.xirsys.com:5349?transport=tcp"
};
for (String turnServer : turnServers)
{
iceServers.add(PeerConnection.IceServer.builder(turnServer).setUsername("e-HcX5lqoTEzSQetY5biyT3YzM45GIl3HK4FQuo3y73xvsLAmts_gF8PylqkwBR6AAAAAFyoMKphbGVhZGVy").setPassword("d97cbeee-5827-11e9-bef6-069f0817bf63").createIceServer());
}
final ArrayList<IceCandidate> iceCandidates = new ArrayList<>();
m_conn = factory.createPeerConnection(iceServers, new PeerConnection.Observer() {
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
// Maybe I need to send these up? http://myhexaville.com/2018/03/19/android-webrtc-signaling/
// Yeah looks like it: https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onicecandidate
Log.i(LOG_TAG, "New candidate");
iceCandidates.add(iceCandidate);
}
@Override
public void onDataChannel(DataChannel dataChannel) {
Log.i(LOG_TAG, "DataChannel created");
final DataChannel currDataChannel = dataChannel;
dataChannel.registerObserver(new DataChannel.Observer() {
@Override
public void onBufferedAmountChange(long l) {
}
@Override
public void onStateChange() {
Log.i(LOG_TAG, "DataChannel state changed: " + currDataChannel.state());
if (currDataChannel.state() == DataChannel.State.OPEN)
{
changeState(State.CONNECTED);
}
}
@Override
public void onMessage(DataChannel.Buffer buffer) {
Log.i(LOG_TAG, "DataChannel message received");
try {
byte[] dataBytes = new byte[buffer.data.capacity()];
buffer.data.get(dataBytes);
String dataStr = new String(dataBytes);
JSONObject dataMessage = new JSONObject(dataStr);
if (dataMessage.has("cardPayload")) {
final String cardPayload = dataMessage.getString("cardPayload");
m_observer.onCardPayload(cardPayload);
}
if (dataMessage.has("hostConfigPayload")) {
final String hostConfigPayload = dataMessage.getString("hostConfigPayload");
m_observer.onHostConfigPayload(hostConfigPayload);
}
} catch (JSONException e) {
e.printStackTrace();
}
//runOnUiThread();
}
});
}
@Override
public void onSignalingChange(PeerConnection.SignalingState signalingState) {
Log.i(LOG_TAG, "SignalingChange: " + signalingState);
}
@Override
public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
Log.i(LOG_TAG, "ConnectionChange: " + iceConnectionState);
// Docs for these states are here: https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState
switch (iceConnectionState)
{
// Means it's temporarily disconnected, it'll try to re-estabilsh connection
case DISCONNECTED:
changeState(State.RECONNECTING);
break;
case CLOSED:
case FAILED:
changeStateToClosed();
break;
}
}
@Override
public void onIceConnectionReceivingChange(boolean b) {
}
@Override
public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
if (iceGatheringState == PeerConnection.IceGatheringState.COMPLETE)
{
try {
JSONObject answer = new JSONObject();
answer.put("sdp", m_conn.getLocalDescription().description);
JSONArray candidatesArray = new JSONArray();
for (int i = 0; i < iceCandidates.size(); i++)
{
IceCandidate iceCandidate = iceCandidates.get(i);
JSONObject candidate = new JSONObject();
candidate.put("candidate", iceCandidate.sdp);
candidate.put("sdpMid", iceCandidate.sdpMid);
candidate.put("sdpMLineIndex", iceCandidate.sdpMLineIndex);
candidatesArray.put(candidate);
}
answer.put("candidates", candidatesArray);
Log.i(LOG_TAG, "Sending answer");
sendAnswer(answer);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
@Override
public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
}
@Override
public void onAddStream(MediaStream mediaStream) {
}
@Override
public void onRemoveStream(MediaStream mediaStream) {
}
@Override
public void onRenegotiationNeeded() {
Log.i(LOG_TAG, "Renegotiation needed");
}
@Override
public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
}
});
SessionDescription offerDescription = null;
try {
offerDescription = new SessionDescription(SessionDescription.Type.OFFER, offerAndCandidates.getString("sdp"));
} catch (JSONException e) {
e.printStackTrace();
m_observer.onConnectFailed("Invalid JSON");
changeStateToClosed();
return;
}
m_conn.setRemoteDescription(new SdpObserver() {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
}
@Override
public void onSetSuccess() {
try
{
// Set the candidates (must be done AFTER setting the description)
JSONArray candidates = offerAndCandidates.getJSONArray("candidates");
for (int i = 0; i < candidates.length(); i++) {
JSONObject candidate = candidates.getJSONObject(i);
m_conn.addIceCandidate(new IceCandidate(candidate.getString("sdpMid"), candidate.getInt("sdpMLineIndex"), candidate.getString("candidate")));
}
onSetOfferSucceeded();
}
catch (JSONException e)
{
e.printStackTrace();
m_observer.onConnectFailed("Invalid JSON");
changeStateToClosed();
return;
}
}
@Override
public void onCreateFailure(String s) {
}
@Override
public void onSetFailure(String s) {
m_observer.onConnectFailed("Setting offer failed");
changeStateToClosed();
}
}, offerDescription);
}