private void connectToHost()

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