mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-26 23:25:20 +03:00
Merge pull request #2545 from nextcloud/add-listener-for-data-channel-messages
Add listener for data channel messages
This commit is contained in:
commit
5786baaeb7
8 changed files with 312 additions and 139 deletions
|
@ -76,7 +76,6 @@ import com.nextcloud.talk.models.json.generic.GenericOverall;
|
|||
import com.nextcloud.talk.models.json.participants.Participant;
|
||||
import com.nextcloud.talk.models.json.participants.ParticipantsOverall;
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessageNick;
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload;
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||
import com.nextcloud.talk.models.json.signaling.Signaling;
|
||||
|
@ -264,9 +263,13 @@ public class CallActivity extends CallBaseActivity {
|
|||
private InternalSignalingMessageSender internalSignalingMessageSender = new InternalSignalingMessageSender();
|
||||
private SignalingMessageSender signalingMessageSender;
|
||||
|
||||
private Map<String, OfferAnswerNickProvider> offerAnswerNickProviders = new HashMap<>();
|
||||
|
||||
private Map<String, SignalingMessageReceiver.CallParticipantMessageListener> callParticipantMessageListeners =
|
||||
new HashMap<>();
|
||||
|
||||
private Map<String, PeerConnectionWrapper.DataChannelMessageListener> dataChannelMessageListeners = new HashMap<>();
|
||||
|
||||
private SignalingMessageReceiver.ParticipantListMessageListener participantListMessageListener = new SignalingMessageReceiver.ParticipantListMessageListener() {
|
||||
|
||||
@Override
|
||||
|
@ -2006,6 +2009,19 @@ public class CallActivity extends CallBaseActivity {
|
|||
new CallActivityCallParticipantMessageListener(sessionId);
|
||||
callParticipantMessageListeners.put(sessionId, callParticipantMessageListener);
|
||||
signalingMessageReceiver.addListener(callParticipantMessageListener, sessionId);
|
||||
|
||||
// DataChannel messages are sent only in video peers; (sender) screen peers do not even open them.
|
||||
PeerConnectionWrapper.DataChannelMessageListener dataChannelMessageListener =
|
||||
new CallActivityDataChannelMessageListener(sessionId);
|
||||
dataChannelMessageListeners.put(sessionId, dataChannelMessageListener);
|
||||
peerConnectionWrapper.addListener(dataChannelMessageListener);
|
||||
}
|
||||
|
||||
if (!publisher && !hasExternalSignalingServer && offerAnswerNickProviders.get(sessionId) == null) {
|
||||
OfferAnswerNickProvider offerAnswerNickProvider = new OfferAnswerNickProvider();
|
||||
offerAnswerNickProviders.put(sessionId, offerAnswerNickProvider);
|
||||
signalingMessageReceiver.addListener(offerAnswerNickProvider.getVideoWebRtcMessageListener(), sessionId, "video");
|
||||
signalingMessageReceiver.addListener(offerAnswerNickProvider.getScreenWebRtcMessageListener(), sessionId, "screen");
|
||||
}
|
||||
|
||||
if (publisher) {
|
||||
|
@ -2032,6 +2048,10 @@ public class CallActivity extends CallBaseActivity {
|
|||
if (!(peerConnectionWrappers = getPeerConnectionWrapperListForSessionId(sessionId)).isEmpty()) {
|
||||
for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrappers) {
|
||||
if (peerConnectionWrapper.getSessionId().equals(sessionId)) {
|
||||
if (!justScreen && VIDEO_STREAM_TYPE_VIDEO.equals(peerConnectionWrapper.getVideoStreamType())) {
|
||||
PeerConnectionWrapper.DataChannelMessageListener dataChannelMessageListener = dataChannelMessageListeners.remove(sessionId);
|
||||
peerConnectionWrapper.removeListener(dataChannelMessageListener);
|
||||
}
|
||||
String videoStreamType = peerConnectionWrapper.getVideoStreamType();
|
||||
if (VIDEO_STREAM_TYPE_SCREEN.equals(videoStreamType) || !justScreen) {
|
||||
runOnUiThread(() -> removeMediaStream(sessionId, videoStreamType));
|
||||
|
@ -2044,6 +2064,12 @@ public class CallActivity extends CallBaseActivity {
|
|||
if (!justScreen) {
|
||||
SignalingMessageReceiver.CallParticipantMessageListener listener = callParticipantMessageListeners.remove(sessionId);
|
||||
signalingMessageReceiver.removeListener(listener);
|
||||
|
||||
OfferAnswerNickProvider offerAnswerNickProvider = offerAnswerNickProviders.remove(sessionId);
|
||||
if (offerAnswerNickProvider != null) {
|
||||
signalingMessageReceiver.removeListener(offerAnswerNickProvider.getVideoWebRtcMessageListener());
|
||||
signalingMessageReceiver.removeListener(offerAnswerNickProvider.getScreenWebRtcMessageListener());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2149,24 +2175,6 @@ public class CallActivity extends CallBaseActivity {
|
|||
toggleMedia(enableVideo, true);
|
||||
}
|
||||
}
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE) {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setNick(peerConnectionEvent.getNick());
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.VIDEO_CHANGE && !isVoiceOnlyCall) {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setStreamEnabled(peerConnectionEvent.getChangeValue());
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.AUDIO_CHANGE) {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setAudioEnabled(peerConnectionEvent.getChangeValue());
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
} else if (peerConnectionEvent.getPeerConnectionEventType() ==
|
||||
PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED) {
|
||||
setCallState(CallStatus.PUBLISHER_FAILED);
|
||||
|
@ -2176,12 +2184,12 @@ public class CallActivity extends CallBaseActivity {
|
|||
}
|
||||
|
||||
private void startSendingNick() {
|
||||
DataChannelMessageNick dataChannelMessage = new DataChannelMessageNick();
|
||||
DataChannelMessage dataChannelMessage = new DataChannelMessage();
|
||||
dataChannelMessage.setType("nickChanged");
|
||||
HashMap<String, String> nickChangedPayload = new HashMap<>();
|
||||
Map<String, String> nickChangedPayload = new HashMap<>();
|
||||
nickChangedPayload.put("userid", conversationUser.getUserId());
|
||||
nickChangedPayload.put("name", conversationUser.getDisplayName());
|
||||
dataChannelMessage.setPayload(nickChangedPayload);
|
||||
dataChannelMessage.setPayloadMap(nickChangedPayload);
|
||||
for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) {
|
||||
if (peerConnectionWrapper.isMCUPublisher()) {
|
||||
Observable
|
||||
|
@ -2196,7 +2204,7 @@ public class CallActivity extends CallBaseActivity {
|
|||
|
||||
@Override
|
||||
public void onNext(@io.reactivex.annotations.NonNull Long aLong) {
|
||||
peerConnectionWrapper.sendNickChannelData(dataChannelMessage);
|
||||
peerConnectionWrapper.sendChannelData(dataChannelMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2265,7 +2273,7 @@ public class CallActivity extends CallBaseActivity {
|
|||
if (hasExternalSignalingServer) {
|
||||
nick = webSocketClient.getDisplayNameForSession(session);
|
||||
} else {
|
||||
nick = peerConnectionWrapper != null ? peerConnectionWrapper.getNick() : "";
|
||||
nick = offerAnswerNickProviders.get(session) != null ? offerAnswerNickProviders.get(session).getNick() : "";
|
||||
}
|
||||
|
||||
String userId4Usage = userId;
|
||||
|
@ -2278,11 +2286,14 @@ public class CallActivity extends CallBaseActivity {
|
|||
}
|
||||
}
|
||||
|
||||
String defaultGuestNick = getResources().getString(R.string.nc_nick_guest);
|
||||
|
||||
ParticipantDisplayItem participantDisplayItem = new ParticipantDisplayItem(baseUrl,
|
||||
userId4Usage,
|
||||
session,
|
||||
connected,
|
||||
nick,
|
||||
defaultGuestNick,
|
||||
mediaStream,
|
||||
videoStreamType,
|
||||
videoStreamEnabled,
|
||||
|
@ -2559,6 +2570,47 @@ public class CallActivity extends CallBaseActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private static class OfferAnswerNickProvider {
|
||||
|
||||
private class WebRtcMessageListener implements SignalingMessageReceiver.WebRtcMessageListener {
|
||||
|
||||
@Override
|
||||
public void onOffer(String sdp, String nick) {
|
||||
(OfferAnswerNickProvider.this).nick = nick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnswer(String sdp, String nick) {
|
||||
(OfferAnswerNickProvider.this).nick = nick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCandidate(String sdpMid, int sdpMLineIndex, String sdp) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEndOfCandidates() {
|
||||
}
|
||||
}
|
||||
|
||||
private final WebRtcMessageListener videoWebRtcMessageListener = new WebRtcMessageListener();
|
||||
private final WebRtcMessageListener screenWebRtcMessageListener = new WebRtcMessageListener();
|
||||
|
||||
private String nick;
|
||||
|
||||
public WebRtcMessageListener getVideoWebRtcMessageListener() {
|
||||
return videoWebRtcMessageListener;
|
||||
}
|
||||
|
||||
public WebRtcMessageListener getScreenWebRtcMessageListener() {
|
||||
return screenWebRtcMessageListener;
|
||||
}
|
||||
|
||||
public String getNick() {
|
||||
return nick;
|
||||
}
|
||||
}
|
||||
|
||||
private class CallActivityCallParticipantMessageListener implements SignalingMessageReceiver.CallParticipantMessageListener {
|
||||
|
||||
private final String sessionId;
|
||||
|
@ -2573,6 +2625,66 @@ public class CallActivity extends CallBaseActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private class CallActivityDataChannelMessageListener implements PeerConnectionWrapper.DataChannelMessageListener {
|
||||
|
||||
private final String participantDisplayItemId;
|
||||
|
||||
private CallActivityDataChannelMessageListener(String sessionId) {
|
||||
// DataChannel messages are sent only in video peers, so the listener only acts on the "video" items.
|
||||
this.participantDisplayItemId = sessionId + "-video";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioOn() {
|
||||
runOnUiThread(() -> {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setAudioEnabled(true);
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioOff() {
|
||||
runOnUiThread(() -> {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setAudioEnabled(false);
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoOn() {
|
||||
runOnUiThread(() -> {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setStreamEnabled(true);
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoOff() {
|
||||
runOnUiThread(() -> {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setStreamEnabled(false);
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNickChanged(String nick) {
|
||||
runOnUiThread(() -> {
|
||||
if (participantDisplayItems.get(participantDisplayItemId) != null) {
|
||||
participantDisplayItems.get(participantDisplayItemId).setNick(nick);
|
||||
participantsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class InternalSignalingMessageSender implements SignalingMessageSender {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,6 +13,7 @@ public class ParticipantDisplayItem {
|
|||
private String session;
|
||||
private boolean connected;
|
||||
private String nick;
|
||||
private final String defaultGuestNick;
|
||||
private String urlForAvatar;
|
||||
private MediaStream mediaStream;
|
||||
private String streamType;
|
||||
|
@ -20,12 +21,13 @@ public class ParticipantDisplayItem {
|
|||
private EglBase rootEglBase;
|
||||
private boolean isAudioEnabled;
|
||||
|
||||
public ParticipantDisplayItem(String baseUrl, String userId, String session, boolean connected, String nick, MediaStream mediaStream, String streamType, boolean streamEnabled, EglBase rootEglBase) {
|
||||
public ParticipantDisplayItem(String baseUrl, String userId, String session, boolean connected, String nick, String defaultGuestNick, MediaStream mediaStream, String streamType, boolean streamEnabled, EglBase rootEglBase) {
|
||||
this.baseUrl = baseUrl;
|
||||
this.userId = userId;
|
||||
this.session = session;
|
||||
this.connected = connected;
|
||||
this.nick = nick;
|
||||
this.defaultGuestNick = defaultGuestNick;
|
||||
this.mediaStream = mediaStream;
|
||||
this.streamType = streamType;
|
||||
this.streamEnabled = streamEnabled;
|
||||
|
@ -61,6 +63,10 @@ public class ParticipantDisplayItem {
|
|||
}
|
||||
|
||||
public String getNick() {
|
||||
if (TextUtils.isEmpty(userId) && TextUtils.isEmpty(nick)) {
|
||||
return defaultGuestNick;
|
||||
}
|
||||
|
||||
return nick;
|
||||
}
|
||||
|
||||
|
@ -78,7 +84,7 @@ public class ParticipantDisplayItem {
|
|||
if (!TextUtils.isEmpty(userId)) {
|
||||
urlForAvatar = ApiUtils.getUrlForAvatar(baseUrl, userId, true);
|
||||
} else {
|
||||
urlForAvatar = ApiUtils.getUrlForGuestAvatar(baseUrl, nick, true);
|
||||
urlForAvatar = ApiUtils.getUrlForGuestAvatar(baseUrl, getNick(), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,6 @@ public class PeerConnectionEvent {
|
|||
}
|
||||
|
||||
public enum PeerConnectionEventType {
|
||||
PEER_CONNECTED, PEER_DISCONNECTED, PEER_CLOSED, SENSOR_FAR, SENSOR_NEAR, NICK_CHANGE, AUDIO_CHANGE, VIDEO_CHANGE, PUBLISHER_FAILED
|
||||
PEER_CONNECTED, PEER_DISCONNECTED, PEER_CLOSED, SENSOR_FAR, SENSOR_NEAR, PUBLISHER_FAILED
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,10 +34,15 @@ import kotlinx.android.parcel.TypeParceler
|
|||
data class DataChannelMessage(
|
||||
@JsonField(name = ["type"])
|
||||
var type: String? = null,
|
||||
/** Can be String or Map<String, String>
|
||||
* Use only for received messages */
|
||||
@JsonField(name = ["payload"])
|
||||
var payload: Any? = null
|
||||
var payload: Any? = null,
|
||||
/** Use only to send messages */
|
||||
@JsonField(name = ["payload"])
|
||||
var payloadMap: Map<String, String>? = null
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null, null)
|
||||
constructor() : this(null, null, null)
|
||||
constructor(type: String) : this(type, null)
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.models.json.signaling
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject
|
||||
import java.util.HashMap
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@JsonObject
|
||||
data class DataChannelMessageNick(
|
||||
@JsonField(name = ["type"])
|
||||
var type: String? = null,
|
||||
@JsonField(name = ["payload"])
|
||||
var payload: HashMap<String, String>? = null
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null, null)
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Daniel Calviño Sánchez
|
||||
* Copyright (C) 2022 Daniel Calviño Sánchez <danxuliu@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.webrtc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helper class to register and notify DataChannelMessageListeners.
|
||||
*
|
||||
* This class is only meant for internal use by PeerConnectionWrapper; listeners must register themselves against
|
||||
* a PeerConnectionWrapper rather than against a DataChannelMessageNotifier.
|
||||
*/
|
||||
public class DataChannelMessageNotifier {
|
||||
|
||||
private final Set<PeerConnectionWrapper.DataChannelMessageListener> dataChannelMessageListeners = new LinkedHashSet<>();
|
||||
|
||||
public synchronized void addListener(PeerConnectionWrapper.DataChannelMessageListener listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("DataChannelMessageListener can not be null");
|
||||
}
|
||||
|
||||
dataChannelMessageListeners.add(listener);
|
||||
}
|
||||
|
||||
public synchronized void removeListener(PeerConnectionWrapper.DataChannelMessageListener listener) {
|
||||
dataChannelMessageListeners.remove(listener);
|
||||
}
|
||||
|
||||
public synchronized void notifyAudioOn() {
|
||||
for (PeerConnectionWrapper.DataChannelMessageListener listener : new ArrayList<>(dataChannelMessageListeners)) {
|
||||
listener.onAudioOn();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void notifyAudioOff() {
|
||||
for (PeerConnectionWrapper.DataChannelMessageListener listener : new ArrayList<>(dataChannelMessageListeners)) {
|
||||
listener.onAudioOff();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void notifyVideoOn() {
|
||||
for (PeerConnectionWrapper.DataChannelMessageListener listener : new ArrayList<>(dataChannelMessageListeners)) {
|
||||
listener.onVideoOn();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void notifyVideoOff() {
|
||||
for (PeerConnectionWrapper.DataChannelMessageListener listener : new ArrayList<>(dataChannelMessageListeners)) {
|
||||
listener.onVideoOff();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void notifyNickChanged(String nick) {
|
||||
for (PeerConnectionWrapper.DataChannelMessageListener listener : new ArrayList<>(dataChannelMessageListeners)) {
|
||||
listener.onNickChanged(nick);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ import android.text.TextUtils;
|
|||
import android.util.Log;
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.events.NetworkEvent;
|
||||
|
@ -384,7 +383,7 @@ public class MagicWebSocketInstance extends WebSocketListener {
|
|||
}
|
||||
}
|
||||
|
||||
return NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_nick_guest);
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getUserIdForSession(String session) {
|
||||
|
|
|
@ -24,16 +24,13 @@
|
|||
package com.nextcloud.talk.webrtc;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.nextcloud.talk.R;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.events.MediaStreamEvent;
|
||||
import com.nextcloud.talk.events.PeerConnectionEvent;
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessage;
|
||||
import com.nextcloud.talk.models.json.signaling.DataChannelMessageNick;
|
||||
import com.nextcloud.talk.models.json.signaling.NCIceCandidate;
|
||||
import com.nextcloud.talk.models.json.signaling.NCMessagePayload;
|
||||
import com.nextcloud.talk.models.json.signaling.NCSignalingMessage;
|
||||
|
@ -59,8 +56,8 @@ import java.io.IOException;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
@ -68,12 +65,26 @@ import javax.inject.Inject;
|
|||
import androidx.annotation.Nullable;
|
||||
import autodagger.AutoInjector;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication.class)
|
||||
public class PeerConnectionWrapper {
|
||||
|
||||
/**
|
||||
* Listener for data channel messages.
|
||||
*
|
||||
* The messages are bound to a specific peer connection, so each listener is expected to handle messages only for
|
||||
* a single peer connection.
|
||||
*
|
||||
* All methods are called on the so called "signaling" thread of WebRTC, which is an internal thread created by the
|
||||
* WebRTC library and NOT the same thread where signaling messages are received.
|
||||
*/
|
||||
public interface DataChannelMessageListener {
|
||||
void onAudioOn();
|
||||
void onAudioOff();
|
||||
void onVideoOn();
|
||||
void onVideoOff();
|
||||
void onNickChanged(String nick);
|
||||
}
|
||||
|
||||
private static final String TAG = PeerConnectionWrapper.class.getCanonicalName();
|
||||
|
||||
private final SignalingMessageReceiver signalingMessageReceiver;
|
||||
|
@ -81,10 +92,11 @@ public class PeerConnectionWrapper {
|
|||
|
||||
private final SignalingMessageSender signalingMessageSender;
|
||||
|
||||
private final DataChannelMessageNotifier dataChannelMessageNotifier = new DataChannelMessageNotifier();
|
||||
|
||||
private List<IceCandidate> iceCandidates = new ArrayList<>();
|
||||
private PeerConnection peerConnection;
|
||||
private String sessionId;
|
||||
private String nick;
|
||||
private final MediaConstraints mediaConstraints;
|
||||
private DataChannel dataChannel;
|
||||
private final MagicSdpObserver magicSdpObserver;
|
||||
|
@ -160,6 +172,21 @@ public class PeerConnectionWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for data channel messages.
|
||||
*
|
||||
* A listener is expected to be added only once. If the same listener is added again it will be notified just once.
|
||||
*
|
||||
* @param listener the DataChannelMessageListener
|
||||
*/
|
||||
public void addListener(DataChannelMessageListener listener) {
|
||||
dataChannelMessageNotifier.addListener(listener);
|
||||
}
|
||||
|
||||
public void removeListener(DataChannelMessageListener listener) {
|
||||
dataChannelMessageNotifier.removeListener(listener);
|
||||
}
|
||||
|
||||
public String getVideoStreamType() {
|
||||
return videoStreamType;
|
||||
}
|
||||
|
@ -203,18 +230,6 @@ public class PeerConnectionWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
public void sendNickChannelData(DataChannelMessageNick dataChannelMessage) {
|
||||
ByteBuffer buffer;
|
||||
if (dataChannel != null) {
|
||||
try {
|
||||
buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes());
|
||||
dataChannel.send(new DataChannel.Buffer(buffer, false));
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendChannelData(DataChannelMessage dataChannelMessage) {
|
||||
ByteBuffer buffer;
|
||||
if (dataChannel != null) {
|
||||
|
@ -235,18 +250,6 @@ public class PeerConnectionWrapper {
|
|||
return sessionId;
|
||||
}
|
||||
|
||||
public String getNick() {
|
||||
if (!TextUtils.isEmpty(nick)) {
|
||||
return nick;
|
||||
} else {
|
||||
return Objects.requireNonNull(NextcloudTalkApplication.Companion.getSharedApplication()).getString(R.string.nc_nick_guest);
|
||||
}
|
||||
}
|
||||
|
||||
private void setNick(String nick) {
|
||||
this.nick = nick;
|
||||
}
|
||||
|
||||
private void sendInitialMediaStatus() {
|
||||
if (localStream != null) {
|
||||
if (localStream.videoTracks.size() == 1 && localStream.videoTracks.get(0).enabled()) {
|
||||
|
@ -288,16 +291,14 @@ public class PeerConnectionWrapper {
|
|||
private class WebRtcMessageListener implements SignalingMessageReceiver.WebRtcMessageListener {
|
||||
|
||||
public void onOffer(String sdp, String nick) {
|
||||
onOfferOrAnswer("offer", sdp, nick);
|
||||
onOfferOrAnswer("offer", sdp);
|
||||
}
|
||||
|
||||
public void onAnswer(String sdp, String nick) {
|
||||
onOfferOrAnswer("answer", sdp, nick);
|
||||
onOfferOrAnswer("answer", sdp);
|
||||
}
|
||||
|
||||
private void onOfferOrAnswer(String type, String sdp, String nick) {
|
||||
setNick(nick);
|
||||
|
||||
private void onOfferOrAnswer(String type, String sdp) {
|
||||
SessionDescription sessionDescriptionWithPreferredCodec;
|
||||
|
||||
boolean isAudio = false;
|
||||
|
@ -350,40 +351,53 @@ public class PeerConnectionWrapper {
|
|||
String strData = new String(bytes);
|
||||
Log.d(TAG, "Got msg: " + strData + " over " + TAG + " " + sessionId);
|
||||
|
||||
DataChannelMessage dataChannelMessage;
|
||||
try {
|
||||
DataChannelMessage dataChannelMessage = LoganSquare.parse(strData, DataChannelMessage.class);
|
||||
|
||||
String internalNick;
|
||||
if ("nickChanged".equals(dataChannelMessage.getType())) {
|
||||
if (dataChannelMessage.getPayload() instanceof String) {
|
||||
internalNick = (String) dataChannelMessage.getPayload();
|
||||
if (!internalNick.equals(nick)) {
|
||||
setNick(internalNick);
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.NICK_CHANGE, sessionId, getNick(), null, videoStreamType));
|
||||
}
|
||||
} else {
|
||||
if (dataChannelMessage.getPayload() != null) {
|
||||
HashMap<String, String> payloadHashMap = (HashMap<String, String>) dataChannelMessage.getPayload();
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.NICK_CHANGE, sessionId, payloadHashMap.get("name"), null, videoStreamType));
|
||||
}
|
||||
}
|
||||
} else if ("audioOn".equals(dataChannelMessage.getType())) {
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.AUDIO_CHANGE, sessionId, null, TRUE, videoStreamType));
|
||||
} else if ("audioOff".equals(dataChannelMessage.getType())) {
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.AUDIO_CHANGE, sessionId, null, FALSE, videoStreamType));
|
||||
} else if ("videoOn".equals(dataChannelMessage.getType())) {
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.VIDEO_CHANGE, sessionId, null, TRUE, videoStreamType));
|
||||
} else if ("videoOff".equals(dataChannelMessage.getType())) {
|
||||
EventBus.getDefault().post(new PeerConnectionEvent(PeerConnectionEvent.PeerConnectionEventType
|
||||
.VIDEO_CHANGE, sessionId, null, FALSE, videoStreamType));
|
||||
}
|
||||
dataChannelMessage = LoganSquare.parse(strData, DataChannelMessage.class);
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Failed to parse data channel message");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ("nickChanged".equals(dataChannelMessage.getType())) {
|
||||
String nick = null;
|
||||
if (dataChannelMessage.getPayload() instanceof String) {
|
||||
nick = (String) dataChannelMessage.getPayload();
|
||||
} else if (dataChannelMessage.getPayload() instanceof Map) {
|
||||
Map<String, String> payloadMap = (Map<String, String>) dataChannelMessage.getPayload();
|
||||
nick = payloadMap.get("name");
|
||||
}
|
||||
|
||||
if (nick != null) {
|
||||
dataChannelMessageNotifier.notifyNickChanged(nick);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ("audioOn".equals(dataChannelMessage.getType())) {
|
||||
dataChannelMessageNotifier.notifyAudioOn();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ("audioOff".equals(dataChannelMessage.getType())) {
|
||||
dataChannelMessageNotifier.notifyAudioOff();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ("videoOn".equals(dataChannelMessage.getType())) {
|
||||
dataChannelMessageNotifier.notifyVideoOn();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ("videoOff".equals(dataChannelMessage.getType())) {
|
||||
dataChannelMessageNotifier.notifyVideoOff();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue