diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 5516758fa..b1ac0dc4e 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -10,6 +10,7 @@
+
@@ -286,6 +287,7 @@
+
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index 423ed9f91..393cec5d4 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -19,6 +19,7 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
+import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
@@ -233,12 +234,14 @@ public class MessageGenerator extends AbstractGenerator {
return packet;
}
- public MessagePacket sessionProposal(JingleConnectionManager.RtpSessionProposal proposal) {
+ public MessagePacket sessionProposal(final JingleConnectionManager.RtpSessionProposal proposal) {
final MessagePacket packet = new MessagePacket();
packet.setTo(proposal.with);
+ packet.setId(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX+proposal.sessionId);
final Element propose = packet.addChild("propose", Namespace.JINGLE_MESSAGE);
propose.setAttribute("id", proposal.sessionId);
propose.addChild("description", Namespace.JINGLE_APPS_RTP);
+ packet.addChild("request", "urn:xmpp:receipts");
return packet;
}
}
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index a53613e28..4b15b8b72 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -43,6 +43,8 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
+import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
+import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
@@ -301,11 +303,18 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
private boolean handleErrorMessage(Account account, MessagePacket packet) {
if (packet.getType() == MessagePacket.TYPE_ERROR) {
- Jid from = packet.getFrom();
- if (from != null) {
+ final Jid from = packet.getFrom();
+ final String id = packet.getId();
+ if (from != null && id != null) {
+ if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX)) {
+ final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX.length());
+ mXmppConnectionService.getJingleConnectionManager()
+ .updateProposedSessionDiscovered(account, from, sessionId, JingleConnectionManager.DeviceDiscoveryState.FAILED);
+ return true;
+ }
mXmppConnectionService.markMessage(account,
from.asBareJid(),
- packet.getId(),
+ id,
Message.STATUS_SEND_FAILED,
extractErrorMessage(packet));
final Element error = packet.findChild("error");
@@ -815,7 +824,11 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (!isTypeGroupChat) {
for (Element child : packet.getChildren()) {
if (Namespace.JINGLE_MESSAGE.equals(child.getNamespace()) && JINGLE_MESSAGE_ELEMENT_NAMES.contains(child.getName())) {
+ if (!account.getJid().asBareJid().equals(from.asBareJid())) {
+ processMessageReceipts(account, packet, query);
+ }
mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child);
+ break;
}
}
}
@@ -831,8 +844,14 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
if (query != null && id != null && packet.getTo() != null) {
query.removePendingReceiptRequest(new ReceiptRequest(packet.getTo(), id));
}
- } else {
- mXmppConnectionService.markMessage(account, from.asBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED);
+ } else if (id != null) {
+ if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX)) {
+ final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX.length());
+ mXmppConnectionService.getJingleConnectionManager()
+ .updateProposedSessionDiscovered(account, from, sessionId, JingleConnectionManager.DeviceDiscoveryState.DISCOVERED);
+ } else {
+ mXmppConnectionService.markMessage(account, from.asBareJid(), id, Message.STATUS_SEND_RECEIVED);
+ }
}
}
Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0");
diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
new file mode 100644
index 000000000..2fdf2ac65
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
@@ -0,0 +1,46 @@
+package eu.siacs.conversations.ui;
+
+import android.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.ActivityRtpSessionBinding;
+
+public class RtpSessionActivity extends XmppActivity {
+
+ public static final String EXTRA_WITH = "with";
+
+ private ActivityRtpSessionBinding binding;
+
+ public void onCreate(Bundle savedInstanceState) {
+ getWindow().addFlags(
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+ super.onCreate(savedInstanceState);
+ this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
+ this.binding.acceptCall.setOnClickListener(this::acceptCall);
+ this.binding.rejectCall.setOnClickListener(this::rejectCall);
+ }
+
+ private void rejectCall(View view) {
+
+ }
+
+ private void acceptCall(View view) {
+
+ }
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java
index 65fce4205..603371394 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java
@@ -11,6 +11,8 @@ import rocks.xmpp.addr.Jid;
public abstract class AbstractJingleConnection {
+ public static final String JINGLE_MESSAGE_ID_PREFIX = "jm-propose-";
+
protected final JingleConnectionManager jingleConnectionManager;
protected final XmppConnectionService xmppConnectionService;
protected final Id id;
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
index 146892e82..bc9e25f2e 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java
@@ -30,7 +30,7 @@ import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import rocks.xmpp.addr.Jid;
public class JingleConnectionManager extends AbstractConnectionManager {
- private final Set rtpSessionProposals = new HashSet<>();
+ private final HashMap rtpSessionProposals = new HashMap<>();
private final Map connections = new ConcurrentHashMap<>();
private HashMap primaryCandidates = new HashMap<>();
@@ -108,7 +108,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
final RtpSessionProposal proposal = new RtpSessionProposal(account, with.asBareJid(), sessionId);
synchronized (rtpSessionProposals) {
- if (rtpSessionProposals.remove(proposal)) {
+ if (rtpSessionProposals.remove(proposal) != null) {
final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, account.getJid());
this.connections.put(id, rtpConnection);
rtpConnection.transitionOrThrow(AbstractJingleConnection.State.PROPOSED);
@@ -190,7 +190,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
public void proposeJingleRtpSession(final Account account, final Contact contact) {
final RtpSessionProposal proposal = RtpSessionProposal.of(account, contact.getJid().asBareJid());
synchronized (this.rtpSessionProposals) {
- this.rtpSessionProposals.add(proposal);
+ this.rtpSessionProposals.put(proposal, DeviceDiscoveryState.SEARCHING);
final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionProposal(proposal);
Log.d(Config.LOGTAG,messagePacket.toString());
mXmppConnectionService.sendMessagePacket(account, messagePacket);
@@ -244,6 +244,23 @@ public class JingleConnectionManager extends AbstractConnectionManager {
}
}
+ public void updateProposedSessionDiscovered(Account account, Jid from, String sessionId, final DeviceDiscoveryState target) {
+ final RtpSessionProposal sessionProposal = new RtpSessionProposal(account,from.asBareJid(),sessionId);
+ synchronized (this.rtpSessionProposals) {
+ final DeviceDiscoveryState currentState = rtpSessionProposals.get(sessionProposal);
+ if (currentState == null) {
+ Log.d(Config.LOGTAG,"unable to find session proposal for session id "+sessionId);
+ return;
+ }
+ if (currentState == DeviceDiscoveryState.DISCOVERED) {
+ Log.d(Config.LOGTAG,"session proposal already at discovered. not going to fall back");
+ return;
+ }
+ this.rtpSessionProposals.put(sessionProposal, target);
+ Log.d(Config.LOGTAG,account.getJid().asBareJid()+": flagging session "+sessionId+" as "+target);
+ }
+ }
+
public static class RtpSessionProposal {
private final Account account;
public final Jid with;
@@ -274,4 +291,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
return Objects.hashCode(account.getJid(), with, sessionId);
}
}
+
+ public enum DeviceDiscoveryState {
+ SEARCHING, DISCOVERED, FAILED
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
index 3e9759c22..f18212df8 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java
@@ -1,5 +1,6 @@
package eu.siacs.conversations.xmpp.jingle;
+import android.content.Intent;
import android.util.Log;
import com.google.common.collect.ImmutableList;
@@ -15,6 +16,7 @@ import java.util.List;
import java.util.Map;
import eu.siacs.conversations.Config;
+import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
@@ -217,13 +219,21 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (originatedFromMyself) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": saw proposal from mysql. ignoring");
} else if (transition(State.PROPOSED)) {
- //TODO start ringing or something
- pickUpCall();
+ startRinging();
} else {
Log.d(Config.LOGTAG, id.account.getJid() + ": ignoring session proposal because already in " + state);
}
}
+ private void startRinging() {
+ Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received call from " + id.with + ". start ringing");
+ final Intent intent = new Intent(xmppConnectionService, RtpSessionActivity.class);
+ intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString());
+ intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ xmppConnectionService.startActivity(intent);
+ }
+
private void receiveProceed(final Jid from, final Element proceed) {
if (from.equals(id.with)) {
if (isInitiator()) {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
index 53210e78c..f4b03a062 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java
@@ -11,6 +11,9 @@ import com.google.common.util.concurrent.SettableFuture;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
+import org.webrtc.Camera1Capturer;
+import org.webrtc.Camera1Enumerator;
+import org.webrtc.CameraVideoCapturer;
import org.webrtc.DataChannel;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
@@ -20,6 +23,9 @@ import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpReceiver;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
+import org.webrtc.VideoCapturer;
+import org.webrtc.VideoSource;
+import org.webrtc.VideoTrack;
import java.util.List;
@@ -30,6 +36,9 @@ import eu.siacs.conversations.Config;
public class WebRTCWrapper {
+ private VideoTrack localVideoTrack = null;
+ private VideoTrack remoteVideoTrack = null;
+
private final EventCallback eventCallback;
private final PeerConnection.Observer peerConnectionObserver = new PeerConnection.Observer() {
@@ -75,6 +84,11 @@ public class WebRTCWrapper {
for(AudioTrack audioTrack : mediaStream.audioTracks) {
Log.d(Config.LOGTAG,"remote? - audioTrack enabled:"+audioTrack.enabled()+" state="+audioTrack.state());
}
+ final List videoTracks = mediaStream.videoTracks;
+ if (videoTracks.size() > 0) {
+ Log.d(Config.LOGTAG, "more than zero remote video tracks found. using first");
+ remoteVideoTrack = videoTracks.get(0);
+ }
}
@Override
@@ -112,15 +126,65 @@ public class WebRTCWrapper {
}
public void initializePeerConnection() {
- final PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().createPeerConnectionFactory();
+ CameraVideoCapturer capturer = null;
+ Camera1Enumerator camera1Enumerator = new Camera1Enumerator();
+ for(String deviceName : camera1Enumerator.getDeviceNames()) {
+ Log.d(Config.LOGTAG,"camera device name: "+deviceName);
+ if (camera1Enumerator.isFrontFacing(deviceName)) {
+ capturer = camera1Enumerator.createCapturer(deviceName, new CameraVideoCapturer.CameraEventsHandler() {
+ @Override
+ public void onCameraError(String s) {
+
+ }
+
+ @Override
+ public void onCameraDisconnected() {
+
+ }
+
+ @Override
+ public void onCameraFreezed(String s) {
+
+ }
+
+ @Override
+ public void onCameraOpening(String s) {
+ Log.d(Config.LOGTAG,"onCameraOpening");
+ }
+
+ @Override
+ public void onFirstFrameAvailable() {
+ Log.d(Config.LOGTAG,"onFirstFrameAvailable");
+ }
+
+ @Override
+ public void onCameraClosed() {
+
+ }
+ });
+ }
+ }
+
+ /*if (capturer != null) {
+ capturer.initialize();
+ Log.d(Config.LOGTAG,"start capturing");
+ capturer.startCapture(800,600,30);
+ }*/
+
+ final VideoSource videoSource = peerConnectionFactory.createVideoSource(false);
+ final VideoTrack videoTrack = peerConnectionFactory.createVideoTrack("my-video-track", videoSource);
+
final AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
final AudioTrack audioTrack = peerConnectionFactory.createAudioTrack("my-audio-track", audioSource);
Log.d(Config.LOGTAG,"audioTrack enabled:"+audioTrack.enabled()+" state="+audioTrack.state());
final MediaStream stream = peerConnectionFactory.createLocalMediaStream("my-media-stream");
stream.addTrack(audioTrack);
+ //stream.addTrack(videoTrack);
+
+ this.localVideoTrack = videoTrack;
final List iceServers = ImmutableList.of(
@@ -136,6 +200,8 @@ public class WebRTCWrapper {
this.peerConnection = peerConnection;
}
+
+
public ListenableFuture createOffer() {
return Futures.transformAsync(getPeerConnectionFuture(), peerConnection -> {
final SettableFuture future = SettableFuture.create();
diff --git a/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png b/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png
new file mode 100644
index 000000000..e1831d7af
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png differ
diff --git a/src/main/res/drawable-hdpi/ic_call_white_48dp.png b/src/main/res/drawable-hdpi/ic_call_white_48dp.png
new file mode 100644
index 000000000..90ead2e45
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_call_white_48dp.png differ
diff --git a/src/main/res/drawable-mdpi/ic_call_end_white_48dp.png b/src/main/res/drawable-mdpi/ic_call_end_white_48dp.png
new file mode 100644
index 000000000..a4fe6889d
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_call_end_white_48dp.png differ
diff --git a/src/main/res/drawable-mdpi/ic_call_white_48dp.png b/src/main/res/drawable-mdpi/ic_call_white_48dp.png
new file mode 100644
index 000000000..ef45e933a
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_call_white_48dp.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png b/src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png
new file mode 100644
index 000000000..8801d0ded
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_call_end_white_48dp.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_call_white_48dp.png b/src/main/res/drawable-xhdpi/ic_call_white_48dp.png
new file mode 100644
index 000000000..b0e020573
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_call_white_48dp.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png
new file mode 100644
index 000000000..c8099a1a1
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_call_end_white_48dp.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_call_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_call_white_48dp.png
new file mode 100644
index 000000000..a8e295a42
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_call_white_48dp.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png
new file mode 100644
index 000000000..a09ec6862
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_call_end_white_48dp.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_call_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_call_white_48dp.png
new file mode 100644
index 000000000..0683f06d1
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_call_white_48dp.png differ
diff --git a/src/main/res/layout/activity_rtp_session.xml b/src/main/res/layout/activity_rtp_session.xml
new file mode 100644
index 000000000..dc7dc4eec
--- /dev/null
+++ b/src/main/res/layout/activity_rtp_session.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index 074b5ae30..4332d1907 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -19,6 +19,7 @@
#ff424242
#ff282828
#fff44336
+ #ffD32F2F
#ffd50000
#ffff8a80
#ffc62828
diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml
index 432f256c0..00ed0b3b5 100644
--- a/src/main/res/values/styles.xml
+++ b/src/main/res/values/styles.xml
@@ -1,11 +1,14 @@
-
+
+
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index 454f0ca6c..054194fbe 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -31,6 +31,7 @@
- 14sp
- 16sp
- 20sp
+ - 45sp
- 16sp
- 5sp
- 18sp