Эх сурвалжийг харах

Improve reconnections & Avatar handling

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 5 жил өмнө
parent
commit
6e68f41d1f

+ 151 - 99
app/src/main/java/com/nextcloud/talk/controllers/CallController.java

@@ -73,6 +73,7 @@ import com.nextcloud.talk.utils.database.user.UserUtils;
 import com.nextcloud.talk.utils.power.PowerManagerUtils;
 import com.nextcloud.talk.utils.preferences.AppPreferences;
 import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder;
+import com.nextcloud.talk.utils.singletons.MerlinTheWizard;
 import com.nextcloud.talk.webrtc.*;
 import com.wooplr.spotlight.SpotlightView;
 import io.reactivex.Observable;
@@ -88,6 +89,7 @@ import org.apache.commons.lang3.StringEscapeUtils;
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
 import org.greenrobot.eventbus.ThreadMode;
+import org.parceler.Parcel;
 import org.webrtc.*;
 import pub.devrel.easypermissions.AfterPermissionGranted;
 
@@ -165,7 +167,7 @@ public class CallController extends BaseController {
     private VideoCapturer videoCapturer;
     private EglBase rootEglBase;
     private boolean leavingCall = false;
-    private boolean inCall = false;
+    private boolean connectedToCall = false;
     private Disposable signalingDisposable;
     private Disposable pingDisposable;
     private List<PeerConnection.IceServer> iceServers;
@@ -199,7 +201,7 @@ public class CallController extends BaseController {
     private SpotlightView spotlightView;
 
     private ExternalSignalingServer externalSignalingServer;
-    private MagicWebSocketInstance webSocketClient ;
+    private MagicWebSocketInstance webSocketClient;
     private WebSocketConnectionHelper webSocketConnectionHelper;
     private boolean hasMCU;
     private boolean hasExternalSignalingServer;
@@ -207,6 +209,15 @@ public class CallController extends BaseController {
 
     private PowerManagerUtils powerManagerUtils;
 
+    private Handler handler;
+
+    private CallStatus currentCallStatus;
+
+    @Parcel
+    public enum CallStatus {
+        CALLING, CALLING_TIMEOUT, ESTABLISHED, RECONNECTING, OFFLINE
+    }
+
     public CallController(Bundle args) {
         super(args);
         NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
@@ -227,6 +238,7 @@ public class CallController extends BaseController {
 
         isFromNotification = TextUtils.isEmpty(roomToken);
         powerManagerUtils = new PowerManagerUtils();
+        currentCallStatus = CallStatus.CALLING;
     }
 
     @Override
@@ -273,13 +285,9 @@ public class CallController extends BaseController {
 
         callControls.setZ(100.0f);
         basicInitialization();
+        initViews();
 
-        if (isFromNotification) {
-            handleFromNotification();
-        } else {
-            initViews();
-            checkPermissions();
-        }
+        initiateCall();
     }
 
     private void basicInitialization() {
@@ -357,7 +365,6 @@ public class CallController extends BaseController {
                             }
                         }
 
-                        initViews();
                         checkPermissions();
                     }
 
@@ -427,7 +434,7 @@ public class CallController extends BaseController {
                 }
             }
 
-            if (!inCall) {
+            if (!connectedToCall) {
                 fetchSignalingSettings();
             }
         } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(),
@@ -466,7 +473,7 @@ public class CallController extends BaseController {
             microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px);
         }
 
-        if (!inCall) {
+        if (!connectedToCall) {
             fetchSignalingSettings();
         }
     }
@@ -484,7 +491,7 @@ public class CallController extends BaseController {
         if (getActivity() != null && (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA) ||
                 EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_MICROPHONE))) {
             checkIfSomeAreApproved();
-        } else if (!inCall) {
+        } else if (!connectedToCall) {
             fetchSignalingSettings();
         }
     }
@@ -634,7 +641,7 @@ public class CallController extends BaseController {
                 toggleMedia(true, false);
             }
 
-            if (isVoiceOnlyCall && !inCall) {
+            if (isVoiceOnlyCall && !connectedToCall) {
                 fetchSignalingSettings();
             }
 
@@ -656,7 +663,7 @@ public class CallController extends BaseController {
 
     @OnClick(R.id.callControlHangupView)
     void onHangupClick() {
-        hangup(false);
+        hangup(true);
     }
 
     @OnClick(R.id.call_control_camera)
@@ -751,7 +758,7 @@ public class CallController extends BaseController {
             }
         }
 
-        if (inCall) {
+        if (connectedToCall) {
             if (!hasMCU) {
                 for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
                     magicPeerConnectionWrapperList.get(i).sendChannelData(new DataChannelMessage(message));
@@ -1056,7 +1063,8 @@ public class CallController extends BaseController {
 
                     @Override
                     public void onNext(GenericOverall genericOverall) {
-                        inCall = true;
+                        connectedToCall = true;
+                        currentCallStatus = CallStatus.CALLING;
 
                         if (connectingView != null) {
                             connectingView.setVisibility(View.GONE);
@@ -1077,8 +1085,8 @@ public class CallController extends BaseController {
                                     .subscribeOn(Schedulers.io())
                                     .observeOn(AndroidSchedulers.mainThread())
                                     .repeatWhen(observable -> observable.delay(5000, TimeUnit.MILLISECONDS))
-                                    .takeWhile(observable -> inCall)
-                                    .retry(3, observable -> inCall)
+                                    .takeWhile(observable -> connectedToCall)
+                                    .retry(3, observable -> connectedToCall)
                                     .subscribe(new Observer<GenericOverall>() {
                                         @Override
                                         public void onSubscribe(Disposable d) {
@@ -1110,7 +1118,7 @@ public class CallController extends BaseController {
 
                         if (!conversationUser.hasSpreedCapabilityWithName("no-ping") && !TextUtils.isEmpty(roomId)) {
                             NotificationUtils.cancelExistingNotifications(getApplicationContext(), conversationUser, roomId);
-                        } else if (!TextUtils.isEmpty(roomToken)){
+                        } else if (!TextUtils.isEmpty(roomToken)) {
                             NotificationUtils.cancelExistingNotifications(getApplicationContext(), conversationUser, roomToken);
                         }
 
@@ -1119,8 +1127,8 @@ public class CallController extends BaseController {
                                     .subscribeOn(Schedulers.io())
                                     .observeOn(AndroidSchedulers.mainThread())
                                     .repeatWhen(observable -> observable)
-                                    .takeWhile(observable -> inCall)
-                                    .retry(3, observable -> inCall)
+                                    .takeWhile(observable -> connectedToCall)
+                                    .retry(3, observable -> connectedToCall)
                                     .subscribe(new Observer<SignalingOverall>() {
                                         @Override
                                         public void onSubscribe(Disposable d) {
@@ -1177,11 +1185,24 @@ public class CallController extends BaseController {
         joinRoomAndCall();
     }
 
+    private void initiateCall() {
+        if (!TextUtils.isEmpty(roomToken)) {
+            checkPermissions();
+        } else {
+            handleFromNotification();
+        }
+    }
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
     public void onMessageEvent(WebSocketCommunicationEvent webSocketCommunicationEvent) {
         switch (webSocketCommunicationEvent.getType()) {
             case "hello":
-                joinRoomAndCall();
+                if (!currentCallStatus.equals(CallStatus.RECONNECTING)) {
+                    if (!webSocketCommunicationEvent.getHashMap().containsKey("oldResumeId")) {
+                        initiateCall();
+                    } else {
+                        // do nothing, let's just continue
+                    }
+                }
                 break;
             case "roomJoined":
                 if (hasExternalSignalingServer) {
@@ -1300,104 +1321,105 @@ public class CallController extends BaseController {
         }
     }
 
-    private void hangup(boolean dueToNetworkChange) {
+    private void hangup(boolean shutDownView) {
 
         leavingCall = true;
-        inCall = false;
+        connectedToCall = false;
+
+        if (shutDownView) {
+            if (videoCapturer != null) {
+                try {
+                    videoCapturer.stopCapture();
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Failed to stop capturing while hanging up");
+                }
+                videoCapturer.dispose();
+                videoCapturer = null;
+            }
 
-        if (videoCapturer != null) {
-            try {
-                videoCapturer.stopCapture();
-            } catch (InterruptedException e) {
-                Log.e(TAG, "Failed to stop capturing while hanging up");
+            if (pipVideoView != null) {
+                pipVideoView.release();
             }
-            videoCapturer.dispose();
-            videoCapturer = null;
-        }
 
-        for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
-            endPeerConnection(magicPeerConnectionWrapperList.get(i).getSessionId(), false);
+            if (audioSource != null) {
+                audioSource.dispose();
+                audioSource = null;
+            }
 
-        }
+            if (audioManager != null) {
+                audioManager.stop();
+                audioManager = null;
+            }
 
-        if (pipVideoView != null) {
-            pipVideoView.release();
-        }
+            if (videoSource != null) {
+                videoSource = null;
+            }
 
-        if (audioSource != null) {
-            audioSource.dispose();
-            audioSource = null;
-        }
+            if (peerConnectionFactory != null) {
+                peerConnectionFactory = null;
+            }
 
-        if (audioManager != null) {
-            audioManager.stop();
-            audioManager = null;
-        }
+            localMediaStream = null;
+            localAudioTrack = null;
+            localVideoTrack = null;
 
-        if (videoSource != null) {
-            videoSource = null;
-        }
 
-        if (peerConnectionFactory != null) {
-            peerConnectionFactory = null;
+            if (TextUtils.isEmpty(credentials) && hasExternalSignalingServer) {
+                WebSocketConnectionHelper.deleteExternalSignalingInstanceForUserEntity(-1);
+            }
         }
 
-        localMediaStream = null;
-        localAudioTrack = null;
-        localVideoTrack = null;
-
-
-        if (TextUtils.isEmpty(credentials) && hasExternalSignalingServer) {
-            WebSocketConnectionHelper.deleteExternalSignalingInstanceForUserEntity(-1);
+        for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
+            endPeerConnection(magicPeerConnectionWrapperList.get(i).getSessionId(), false);
         }
 
-        if (!dueToNetworkChange) {
-            hangupNetworkCalls();
-        } else {
-            if (getActivity() != null) {
-                getActivity().finish();
-            }
-        }
+        hangupNetworkCalls(shutDownView);
     }
 
-    private void hangupNetworkCalls() {
-        ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken))
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(new Observer<GenericOverall>() {
-                    @Override
-                    public void onSubscribe(Disposable d) {
-
-                    }
+    private void hangupNetworkCalls(boolean shutDownView) {
+        if (MerlinTheWizard.isConnectedToInternet()) {
+            ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken))
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new Observer<GenericOverall>() {
+                        @Override
+                        public void onSubscribe(Disposable d) {
 
-                    @Override
-                    public void onNext(GenericOverall genericOverall) {
-                        if (hasExternalSignalingServer) {
-                            webSocketClient.joinRoomWithRoomTokenAndSession("", callSession);
                         }
 
-                        if (isMultiSession) {
-                            if (getActivity() != null) {
-                                getActivity().finish();
+                        @Override
+                        public void onNext(GenericOverall genericOverall) {
+                            if (!TextUtils.isEmpty(credentials) && hasExternalSignalingServer) {
+                                webSocketClient.joinRoomWithRoomTokenAndSession("", callSession);
+                            }
+
+                            if (isMultiSession) {
+                                if (shutDownView && getActivity() != null) {
+                                    getActivity().finish();
+                                } else if (!shutDownView && currentCallStatus.equals(CallStatus.RECONNECTING)) {
+                                    initiateCall();
+                                }
+                            } else {
+                                leaveRoom(shutDownView);
                             }
-                        } else {
-                            leaveRoom();
                         }
-                    }
 
-                    @Override
-                    public void onError(Throwable e) {
+                        @Override
+                        public void onError(Throwable e) {
 
-                    }
+                        }
 
-                    @Override
-                    public void onComplete() {
+                        @Override
+                        public void onComplete() {
 
-                    }
-                });
+                        }
+                    });
+        } else if (shutDownView && getActivity() != null) {
+            getActivity().finish();
+        }
     }
 
-    private void leaveRoom() {
+    private void leaveRoom(boolean shutDownView) {
         ncApi.leaveRoom(credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, roomToken))
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
@@ -1409,7 +1431,7 @@ public class CallController extends BaseController {
 
                     @Override
                     public void onNext(GenericOverall genericOverall) {
-                        if (getActivity() != null) {
+                        if (shutDownView && getActivity() != null) {
                             getActivity().finish();
                         }
                     }
@@ -1431,7 +1453,7 @@ public class CallController extends BaseController {
             videoCapturer.startCapture(1280, 720, 30);
         }
     }
-    
+
     private void processUsersInRoom(List<HashMap<String, Object>> users) {
         List<String> newSessions = new ArrayList<>();
         Set<String> oldSesssions = new HashSet<>();
@@ -1633,6 +1655,7 @@ public class CallController extends BaseController {
             pipVideoView.setLayoutParams(layoutParams);
         }
     }
+
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) {
         if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType
@@ -1647,7 +1670,7 @@ public class CallController extends BaseController {
                 boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
                         .PeerConnectionEventType.SENSOR_FAR) && videoOn;
                 if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA) &&
-                        inCall && videoOn
+                        connectedToCall && videoOn
                         && enableVideo != localVideoTrack.enabled()) {
                     toggleMedia(enableVideo, true);
                 }
@@ -1678,7 +1701,7 @@ public class CallController extends BaseController {
                 int finalI = i;
                 Observable
                         .interval(1, TimeUnit.SECONDS)
-                        .takeWhile(observer -> inCall)
+                        .takeWhile(observer -> connectedToCall)
                         .observeOn(Schedulers.io())
                         .doOnNext(n -> magicPeerConnectionWrapperList.get(finalI).sendChannelData(dataChannelMessage));
                 break;
@@ -1796,14 +1819,23 @@ public class CallController extends BaseController {
             if (relativeLayout != null) {
                 SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView);
 
-                if (participantMap.containsKey(session) && avatarImageView.getDrawable() == null) {
+                String userId;
+
+                if (hasMCU) {
+                    userId = webSocketClient.getUserIdForSession(session);
+                } else {
+                    userId = participantMap.get(session).getUserId();
+                }
+
+                if (!TextUtils.isEmpty(userId)) {
 
                     if (getActivity() != null) {
+                        avatarImageView.setController(null);
+
                         DraweeController draweeController = Fresco.newDraweeControllerBuilder()
                                 .setOldController(avatarImageView.getController())
-                                .setAutoPlayAnimations(true)
                                 .setImageRequest(DisplayUtils.getImageRequestForUrl(ApiUtils.getUrlForAvatarWithName(baseUrl,
-                                        participantMap.get(session).getUserId(),
+                                        userId,
                                         R.dimen.avatar_size_big), null))
                                 .build();
                         avatarImageView.setController(draweeController);
@@ -1958,4 +1990,24 @@ public class CallController extends BaseController {
             showCallControls();
         }
     }
+
+    @Subscribe(threadMode = ThreadMode.BACKGROUND)
+    public void onMessageEvent(NetworkEvent networkEvent) {
+        if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) {
+            if (handler != null) {
+                handler.removeCallbacksAndMessages(null);
+            }
+
+            currentCallStatus = CallStatus.RECONNECTING;
+            hangupNetworkCalls(false);
+
+        } else if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) {
+            if (handler != null) {
+                handler.removeCallbacksAndMessages(null);
+            }
+
+            currentCallStatus = CallStatus.OFFLINE;
+            hangup(false);
+        }
+    }
 }

+ 12 - 9
app/src/main/java/com/nextcloud/talk/utils/singletons/MerlinTheWizard.java

@@ -34,7 +34,6 @@ import javax.inject.Inject;
 @AutoInjector(NextcloudTalkApplication.class)
 public class MerlinTheWizard {
     private static Merlin merlin;
-    private static MerlinsBeard merlinsBeard;
 
     private UserEntity currentUserEntity;
 
@@ -47,10 +46,16 @@ public class MerlinTheWizard {
     @Inject
     UserUtils userUtils;
 
+    private static boolean isConnectedToInternet;
+
     public MerlinTheWizard() {
         NextcloudTalkApplication.getSharedApplication().getComponentApplication().inject(this);
     }
 
+    public static boolean isConnectedToInternet() {
+        return isConnectedToInternet;
+    }
+
     public void initMerlin() {
         if (userUtils.anyUserExists() && (currentUserEntity == null ||
                 (userUtils.getCurrentUser().getId() != currentUserEntity.getId()))) {
@@ -60,14 +65,13 @@ public class MerlinTheWizard {
     }
 
     public Merlin getMerlin() {
-        return merlin;
-    }
+        if (merlin == null) {
+            initMerlin();
+        }
 
-    public MerlinsBeard getMerlinsBeard() {
-        return merlinsBeard;
+        return merlin;
     }
 
-
     private void setupMerlinForCurrentUserEntity() {
         Endpoint endpoint = Endpoint.from(currentUserEntity.getBaseUrl() + "/index.php/204");
         ResponseCodeValidator responseCodeValidator =
@@ -81,12 +85,10 @@ public class MerlinTheWizard {
 
         merlin.bind();
 
-        merlinsBeard = new MerlinsBeard.Builder().withEndpoint(Endpoint.from(currentUserEntity.getBaseUrl() +
-                "/index.php/204")).withResponseCodeValidator(new ResponseCodeValidator.CaptivePortalResponseCodeValidator()).build(context);
-
         merlin.registerConnectable(new Connectable() {
             @Override
             public void onConnect() {
+                isConnectedToInternet = true;
                 eventBus.post(new NetworkEvent(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED));
             }
         });
@@ -94,6 +96,7 @@ public class MerlinTheWizard {
         merlin.registerDisconnectable(new Disconnectable() {
             @Override
             public void onDisconnect() {
+                isConnectedToInternet = false;
                 eventBus.post(new NetworkEvent(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED));
             }
         });

+ 39 - 8
app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java

@@ -36,6 +36,7 @@ import com.nextcloud.talk.events.WebSocketCommunicationEvent;
 import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
 import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
 import com.nextcloud.talk.utils.LoggingUtils;
+import com.nextcloud.talk.utils.singletons.MerlinTheWizard;
 import org.greenrobot.eventbus.EventBus;
 import org.webrtc.*;
 
@@ -53,7 +54,7 @@ public class MagicPeerConnectionWrapper {
     private PeerConnection peerConnection;
     private String sessionId;
     private String nick;
-    private MediaConstraints mediaConstraints;
+    private MediaConstraints sdpConstraints;
     private DataChannel magicDataChannel;
     private MagicSdpObserver magicSdpObserver;
     private MediaStream remoteMediaStream;
@@ -65,14 +66,17 @@ public class MagicPeerConnectionWrapper {
 
     private MediaStream localMediaStream;
     private boolean isMCUPublisher;
+    private boolean hasMCU;
     private String videoStreamType;
 
+    private int connectionAttempts = 0;
+
     @Inject
     Context context;
 
     public MagicPeerConnectionWrapper(PeerConnectionFactory peerConnectionFactory,
                                       List<PeerConnection.IceServer> iceServerList,
-                                      MediaConstraints mediaConstraints,
+                                      MediaConstraints sdpConstraints,
                                       String sessionId, String localSession, @Nullable MediaStream mediaStream,
                                       boolean isMCUPublisher, boolean hasMCU, String videoStreamType) {
 
@@ -80,15 +84,16 @@ public class MagicPeerConnectionWrapper {
 
         this.localMediaStream = mediaStream;
         this.videoStreamType = videoStreamType;
+        this.hasMCU = hasMCU;
 
         this.sessionId = sessionId;
-        this.mediaConstraints = mediaConstraints;
+        this.sdpConstraints = sdpConstraints;
 
         magicSdpObserver = new MagicSdpObserver();
         hasInitiated = sessionId.compareTo(localSession) < 0;
         this.isMCUPublisher = isMCUPublisher;
-
-        peerConnection = peerConnectionFactory.createPeerConnection(iceServerList, mediaConstraints,
+        
+        peerConnection = peerConnectionFactory.createPeerConnection(iceServerList, sdpConstraints,
                 new MagicPeerConnectionObserver());
 
         if (peerConnection != null) {
@@ -102,13 +107,13 @@ public class MagicPeerConnectionWrapper {
                 magicDataChannel = peerConnection.createDataChannel("status", init);
                 magicDataChannel.registerObserver(new MagicDataChannelObserver());
                 if (isMCUPublisher) {
-                    peerConnection.createOffer(magicSdpObserver, mediaConstraints);
+                    peerConnection.createOffer(magicSdpObserver, sdpConstraints);
                 } else if (hasMCU) {
                     HashMap<String, String> hashMap = new HashMap<>();
                     hashMap.put("sessionId", sessionId);
                     EventBus.getDefault().post(new WebSocketCommunicationEvent("peerReadyForRequestingOffer", hashMap));
                 } else if (hasInitiated) {
-                    peerConnection.createOffer(magicSdpObserver, mediaConstraints);
+                    peerConnection.createOffer(magicSdpObserver, sdpConstraints);
 
                 }
             }
@@ -287,6 +292,26 @@ public class MagicPeerConnectionWrapper {
         }
     }
 
+    private void restartIce() {
+        if (connectionAttempts <= 5) {
+            if (!hasMCU || isMCUPublisher) {
+                MediaConstraints.KeyValuePair iceRestartConstraint =
+                        new MediaConstraints.KeyValuePair("IceRestart", "true");
+
+                if (sdpConstraints.mandatory.contains(iceRestartConstraint)) {
+                    sdpConstraints.mandatory.add(iceRestartConstraint);
+                }
+
+                peerConnection.createOffer(magicSdpObserver, sdpConstraints);
+            } else {
+                // we have an MCU and this is not the publisher
+                // Do something if we have an MCU
+            }
+
+            connectionAttempts++;
+        }
+    }
+
     private class MagicPeerConnectionObserver implements PeerConnection.Observer {
         private final String TAG = "MagicPeerConnectionObserver";
 
@@ -300,6 +325,7 @@ public class MagicPeerConnectionWrapper {
                     "iceConnectionChangeTo: " + iceConnectionState.name() + " over " + peerConnection.hashCode() + " " + sessionId);
 
             if (iceConnectionState.equals(PeerConnection.IceConnectionState.CONNECTED)) {
+                connectionAttempts = 0;
                 /*EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
                         .PEER_CONNECTED, sessionId, null, null));*/
 
@@ -314,6 +340,11 @@ public class MagicPeerConnectionWrapper {
             } else if (iceConnectionState.equals(PeerConnection.IceConnectionState.CLOSED)) {
                 EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
                         .PEER_CLOSED, sessionId, null, null, videoStreamType));
+                connectionAttempts = 0;
+            } else if (iceConnectionState.equals(PeerConnection.IceConnectionState.FAILED)) {
+                if (MerlinTheWizard.isConnectedToInternet() && connectionAttempts < 5) {
+                    restartIce();
+                }
             }
         }
 
@@ -413,7 +444,7 @@ public class MagicPeerConnectionWrapper {
         public void onSetSuccess() {
             if (peerConnection != null) {
                 if (peerConnection.getLocalDescription() == null) {
-                    peerConnection.createAnswer(magicSdpObserver, mediaConstraints);
+                    peerConnection.createAnswer(magicSdpObserver, sdpConstraints);
                 }
 
                 if (peerConnection.getRemoteDescription() != null) {

+ 33 - 19
app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java

@@ -30,15 +30,13 @@ import com.nextcloud.talk.application.NextcloudTalkApplication;
 import com.nextcloud.talk.events.NetworkEvent;
 import com.nextcloud.talk.events.WebSocketCommunicationEvent;
 import com.nextcloud.talk.models.database.UserEntity;
+import com.nextcloud.talk.models.json.participants.Participant;
 import com.nextcloud.talk.models.json.signaling.NCMessageWrapper;
 import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
 import com.nextcloud.talk.models.json.websocket.*;
 import com.nextcloud.talk.utils.LoggingUtils;
 import com.nextcloud.talk.utils.MagicMap;
 import com.nextcloud.talk.utils.singletons.MerlinTheWizard;
-import com.novoda.merlin.Endpoint;
-import com.novoda.merlin.MerlinsBeard;
-import com.novoda.merlin.ResponseCodeValidator;
 import okhttp3.*;
 import okio.ByteString;
 import org.greenrobot.eventbus.EventBus;
@@ -80,10 +78,8 @@ public class MagicWebSocketInstance extends WebSocketListener {
     private int restartCount = 0;
     private boolean reconnecting = false;
 
-    private HashMap<String, String> displayNameHashMap;
-    private HashMap<String, String> userIdSesssionHashMap;
+    private HashMap<String, Participant> usersHashMap;
 
-    private MerlinTheWizard merlinTheWizard;
     private List<String> messagesQueue = new ArrayList<>();
 
     MagicWebSocketInstance(UserEntity conversationUser, String connectionUrl, String webSocketTicket) {
@@ -93,11 +89,9 @@ public class MagicWebSocketInstance extends WebSocketListener {
         this.conversationUser = conversationUser;
         this.webSocketTicket = webSocketTicket;
         this.webSocketConnectionHelper = new WebSocketConnectionHelper();
-        this.displayNameHashMap = new HashMap<>();
-        this.userIdSesssionHashMap = new HashMap<>();
+        this.usersHashMap = new HashMap<>();
         magicMap = new MagicMap();
 
-        merlinTheWizard = new MerlinTheWizard();
         connected = false;
         eventBus.register(this);
 
@@ -133,7 +127,7 @@ public class MagicWebSocketInstance extends WebSocketListener {
     private void restartWebSocket() {
         reconnecting = true;
 
-        if (merlinTheWizard.getMerlinsBeard().hasInternetAccess()) {
+        if (MerlinTheWizard.isConnectedToInternet()) {
             Request request = new Request.Builder().url(connectionUrl).build();
             okHttpClient.newWebSocket(request, this);
             restartCount++;
@@ -155,6 +149,7 @@ public class MagicWebSocketInstance extends WebSocketListener {
                         connected = true;
                         reconnecting = false;
                         restartCount = 0;
+                        String oldResumeId = resumeId;
                         HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage = LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class);
                         resumeId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId();
                         sessionId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId();
@@ -165,7 +160,11 @@ public class MagicWebSocketInstance extends WebSocketListener {
                         }
 
                         messagesQueue = new ArrayList<>();
-                        eventBus.post(new WebSocketCommunicationEvent("hello", null));
+                        HashMap<String, String> helloHasHap = new HashMap<>();
+                        if (!TextUtils.isEmpty(oldResumeId)) {
+                            helloHasHap.put("oldResumeId", oldResumeId);
+                        }
+                        eventBus.post(new WebSocketCommunicationEvent("hello", helloHasHap));
                         break;
                     case "error":
                         ErrorOverallWebSocketMessage errorOverallWebSocketMessage = LoganSquare.parse(text, ErrorOverallWebSocketMessage.class);
@@ -187,8 +186,7 @@ public class MagicWebSocketInstance extends WebSocketListener {
                             joinRoomHashMap.put("roomToken", currentRoomToken);
                             eventBus.post(new WebSocketCommunicationEvent("roomJoined", joinRoomHashMap));
                         } else {
-                            userIdSesssionHashMap = new HashMap<>();
-                            displayNameHashMap = new HashMap<>();
+                            usersHashMap = new HashMap<>();
                         }
                         break;
                     case "event":
@@ -216,11 +214,14 @@ public class MagicWebSocketInstance extends WebSocketListener {
                                     } else if (eventOverallWebSocketMessage.getEventMap().get("type").equals("join")) {
                                         List<HashMap<String, Object>> joinEventMap = (List<HashMap<String, Object>>) eventOverallWebSocketMessage.getEventMap().get("join");
                                         HashMap<String, Object> internalHashMap;
+                                        Participant participant;
                                         for (int i = 0; i < joinEventMap.size(); i++) {
                                             internalHashMap = joinEventMap.get(i);
                                             HashMap<String, Object> userMap = (HashMap<String, Object>) internalHashMap.get("user");
-                                            displayNameHashMap.put((String) internalHashMap.get("sessionid"), (String) userMap.get("displayname"));
-                                            userIdSesssionHashMap.put((String) internalHashMap.get("userid"), (String) internalHashMap.get("sessionid"));
+                                            participant = new Participant();
+                                            participant.setUserId((String) internalHashMap.get("userid"));
+                                            participant.setDisplayName((String) userMap.get("displayname"));
+                                            usersHashMap.put((String) internalHashMap.get("sessionid"), participant);
                                         }
                                     }
                                     break;
@@ -359,15 +360,29 @@ public class MagicWebSocketInstance extends WebSocketListener {
     }
 
     public String getDisplayNameForSession(String session) {
-        if (displayNameHashMap.containsKey(session)) {
-            return displayNameHashMap.get(session);
+        if (usersHashMap.containsKey(session)) {
+            return usersHashMap.get(session).getDisplayName();
         }
 
         return NextcloudTalkApplication.getSharedApplication().getString(R.string.nc_nick_guest);
     }
 
     public String getSessionForUserId(String userId) {
-        return userIdSesssionHashMap.get(userId);
+        for (String session : usersHashMap.keySet()) {
+            if (userId.equals(usersHashMap.get(session).getUserId())) {
+                return session;
+            }
+        }
+
+        return "";
+    }
+
+    public String getUserIdForSession(String session) {
+        if (usersHashMap.containsKey(session)) {
+            return usersHashMap.get(session).getUserId();
+        }
+
+        return "";
     }
 
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
@@ -376,5 +391,4 @@ public class MagicWebSocketInstance extends WebSocketListener {
             restartWebSocket();
         }
     }
-
 }

+ 1 - 1
app/src/main/res/values/setup.xml

@@ -23,7 +23,7 @@
     <!-- Set before a release -->
     <string name="nc_talk_database_encryption_key" translatable="false">HvAfHtAy/QdFYqAWFFXa1VV_Iv6ZQ1.tf5swMc^45wS_vz=Wm[oyRP5D-</string>
     <string name="nc_talk_login_scheme" translatable="false">nc</string>
-    <bool name="nc_is_debug">true</bool>
+    <bool name="nc_is_debug">false</bool>
 
     <string name="nc_app_name">Nextcloud Talk</string>
     <string name="nc_server_product_name">Nextcloud</string>