more conditions under which to print call log

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/parser/MessageParser.java                |  2 
src/main/java/eu/siacs/conversations/utils/UIHelper.java                      |  9 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java | 44 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java     | 48 
src/main/res/values/strings.xml                                               |  1 
5 files changed, 84 insertions(+), 20 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/parser/MessageParser.java ๐Ÿ”—

@@ -832,7 +832,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
                         if (!account.getJid().asBareJid().equals(from.asBareJid())) {
                             processMessageReceipts(account, packet, query);
                         }
-                        mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child);
+                        mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child, serverMsgId, timestamp);
                         break;
                     }
                 }

src/main/java/eu/siacs/conversations/utils/UIHelper.java ๐Ÿ”—

@@ -31,6 +31,7 @@ import eu.siacs.conversations.entities.ListItem;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.MucOptions;
 import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.RtpSessionStatus;
 import eu.siacs.conversations.entities.Transferable;
 import eu.siacs.conversations.services.ExportBackupService;
 import rocks.xmpp.addr.Jid;
@@ -300,7 +301,13 @@ public class UIHelper {
 		} else if (message.isFileOrImage()) {
 			return new Pair<>(getFileDescriptionString(context, message), true);
 		} else if (message.getType() == Message.TYPE_RTP_SESSION) {
-			return new Pair<>(context.getString(message.getStatus() == Message.STATUS_RECEIVED ? R.string.incoming_call : R.string.outgoing_call), true);
+			RtpSessionStatus rtpSessionStatus = RtpSessionStatus.of(message.getBody());
+			final boolean received = message.getStatus() == Message.STATUS_RECEIVED;
+			if (!rtpSessionStatus.successful && received) {
+				return new Pair<>(context.getString(R.string.missed_call),true);
+			} else {
+				return new Pair<>(context.getString(received ? R.string.incoming_call : R.string.outgoing_call), true);
+			}
 		} else {
 			final String body = MessageUtils.filterLtrRtl(message.getBody());
 			if (body.startsWith(Message.ME_COMMAND)) {

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java ๐Ÿ”—

@@ -14,6 +14,8 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Conversational;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.Transferable;
 import eu.siacs.conversations.services.AbstractConnectionManager;
@@ -108,7 +110,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
         account.getXmppConnection().sendIqPacket(response, null);
     }
 
-    public void deliverMessage(final Account account, final Jid to, final Jid from, final Element message) {
+    public void deliverMessage(final Account account, final Jid to, final Jid from, final Element message, String serverMsgId, long timestamp) {
         Preconditions.checkArgument(Namespace.JINGLE_MESSAGE.equals(message.getNamespace()));
         final String sessionId = message.getAttribute("id");
         if (sessionId == null) {
@@ -120,7 +122,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                     final JingleRtpConnection rtpConnection = (JingleRtpConnection) connection;
                     final AbstractJingleConnection.Id id = connection.getId();
                     if (id.account == account && id.sessionId.equals(sessionId)) {
-                        rtpConnection.deliveryMessage(from, message);
+                        rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
                         return;
                     }
                 }
@@ -141,7 +143,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
         final AbstractJingleConnection existingJingleConnection = connections.get(id);
         if (existingJingleConnection != null) {
             if (existingJingleConnection instanceof JingleRtpConnection) {
-                ((JingleRtpConnection) existingJingleConnection).deliveryMessage(from, message);
+                ((JingleRtpConnection) existingJingleConnection).deliveryMessage(from, message, serverMsgId, timestamp);
             } else {
                 Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + existingJingleConnection.getClass().getName() + " does not support jingle messages");
             }
@@ -162,7 +164,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                 } else {
                     final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, from);
                     this.connections.put(id, rtpConnection);
-                    rtpConnection.deliveryMessage(from, message);
+                    rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
                 }
             } else {
                 Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to react to proposed " + namespace + " session");
@@ -175,7 +177,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
                     final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, account.getJid());
                     this.connections.put(id, rtpConnection);
                     rtpConnection.transitionOrThrow(AbstractJingleConnection.State.PROPOSED);
-                    rtpConnection.deliveryMessage(from, message);
+                    rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
                 } else {
                     Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver proceed");
                 }
@@ -184,6 +186,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
             final RtpSessionProposal proposal = new RtpSessionProposal(account, from.asBareJid(), sessionId);
             synchronized (rtpSessionProposals) {
                 if (rtpSessionProposals.remove(proposal) != null) {
+                    writeLogMissedOutgoing(account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
                     mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, proposal.with, proposal.sessionId, RtpEndUserState.DECLINED_OR_BUSY);
                 } else {
                     Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver reject");
@@ -195,6 +198,35 @@ public class JingleConnectionManager extends AbstractConnectionManager {
 
     }
 
+    private void writeLogMissedOutgoing(final Account account, Jid with, final String sessionId, String serverMsgId, long timestamp) {
+        final Conversation conversation = mXmppConnectionService.findOrCreateConversation(
+                account,
+                with.asBareJid(),
+                false,
+                false
+        );
+        final Message message = new Message(
+                conversation,
+                Message.STATUS_SEND,
+                Message.TYPE_RTP_SESSION,
+                sessionId
+        );
+        message.setServerMsgId(serverMsgId);
+        message.setTime(timestamp);
+        writeMessage(message);
+    }
+
+    private void writeMessage(final Message message) {
+        final Conversational conversational = message.getConversation();
+        if (conversational instanceof Conversation) {
+            ((Conversation) conversational).add(message);
+            mXmppConnectionService.databaseBackend.createMessage(message);
+            mXmppConnectionService.updateConversationUi();
+        } else {
+            throw new IllegalStateException("Somehow the conversation in a message was a stub");
+        }
+    }
+
     public void startJingleFileTransfer(final Message message) {
         Preconditions.checkArgument(message.isFileOrImage(), "Message is not of type file or image");
         final Transferable old = message.getTransferable();
@@ -271,7 +303,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
             if (matchingProposal != null) {
                 this.rtpSessionProposals.remove(matchingProposal);
                 final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(matchingProposal);
-                Log.d(Config.LOGTAG, messagePacket.toString());
+                writeLogMissedOutgoing(account, matchingProposal.with, matchingProposal.sessionId, null, System.currentTimeMillis());
                 mXmppConnectionService.sendMessagePacket(account, messagePacket);
 
             }

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java ๐Ÿ”—

@@ -371,23 +371,23 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         send(sessionAccept);
     }
 
-    void deliveryMessage(final Jid from, final Element message) {
+    void deliveryMessage(final Jid from, final Element message, final String serverMessageId, final long timestamp) {
         Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": delivered message to JingleRtpConnection " + message);
         switch (message.getName()) {
             case "propose":
-                receivePropose(from, message);
+                receivePropose(from, serverMessageId, timestamp);
                 break;
             case "proceed":
-                receiveProceed(from, message);
+                receiveProceed(from, serverMessageId, timestamp);
                 break;
             case "retract":
-                receiveRetract(from, message);
+                receiveRetract(from, serverMessageId, timestamp);
                 break;
             case "reject":
-                receiveReject(from, message);
+                receiveReject(from, serverMessageId, timestamp);
                 break;
             case "accept":
-                receiveAccept(from, message);
+                receiveAccept(from, serverMessageId, timestamp);
                 break;
             default:
                 break;
@@ -403,10 +403,16 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         }
     }
 
-    private void receiveAccept(Jid from, Element message) {
+    private void receiveAccept(final Jid from, final String serverMsgId, final long timestamp) {
         final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
         if (originatedFromMyself) {
             if (transition(State.ACCEPTED)) {
+                if (serverMsgId != null) {
+                    this.message.setServerMsgId(serverMsgId);
+                }
+                this.message.setTime(timestamp);
+                this.message.setCarbon(true); //indicate that call was accepted on other device
+                this.writeLogMessageSuccess(0);
                 this.xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
                 this.jingleConnectionManager.finishConnection(this);
             } else {
@@ -417,13 +423,19 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         }
     }
 
-    private void receiveReject(Jid from, Element message) {
+    private void receiveReject(Jid from, String serverMsgId, long timestamp) {
         final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
         //reject from another one of my clients
         if (originatedFromMyself) {
             if (transition(State.REJECTED)) {
                 this.xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
                 this.jingleConnectionManager.finishConnection(this);
+                if (serverMsgId != null) {
+                    this.message.setServerMsgId(serverMsgId);
+                }
+                this.message.setTime(timestamp);
+                this.message.setCarbon(true); //indicate that call was rejected on other device
+                writeLogMessageMissed();
             } else {
                 Log.d(Config.LOGTAG, "not able to transition into REJECTED because already in " + this.state);
             }
@@ -432,12 +444,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         }
     }
 
-    private void receivePropose(final Jid from, final Element propose) {
+    private void receivePropose(final Jid from, final String serverMsgId, final long timestamp) {
         final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid());
-        //TODO we can use initiator logic here
         if (originatedFromMyself) {
             Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": saw proposal from mysql. ignoring");
         } else if (transition(State.PROPOSED)) {
+            if (serverMsgId != null) {
+                this.message.setServerMsgId(serverMsgId);
+            }
+            this.message.setTime(timestamp);
             startRinging();
         } else {
             Log.d(Config.LOGTAG, id.account.getJid() + ": ignoring session proposal because already in " + state);
@@ -449,10 +464,14 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         xmppConnectionService.getNotificationService().showIncomingCallNotification(id);
     }
 
-    private void receiveProceed(final Jid from, final Element proceed) {
+    private void receiveProceed(final Jid from, final String serverMsgId, final long timestamp) {
         if (from.equals(id.with)) {
             if (isInitiator()) {
                 if (transition(State.PROCEED)) {
+                    if (serverMsgId != null) {
+                        this.message.setServerMsgId(serverMsgId);
+                    }
+                    this.message.setTime(timestamp);
                     this.sendSessionInitiate(State.SESSION_INITIALIZED_PRE_APPROVED);
                 } else {
                     Log.d(Config.LOGTAG, String.format("%s: ignoring proceed because already in %s", id.account.getJid().asBareJid(), this.state));
@@ -471,11 +490,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         }
     }
 
-    private void receiveRetract(final Jid from, final Element retract) {
+    private void receiveRetract(final Jid from, final String serverMsgId, final long timestamp) {
         if (from.equals(id.with)) {
             if (transition(State.RETRACTED)) {
                 xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
                 Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": session with " + id.with + " has been retracted");
+                if (serverMsgId != null) {
+                    this.message.setServerMsgId(serverMsgId);
+                }
+                this.message.setTime(timestamp);
                 writeLogMessageMissed();
                 jingleConnectionManager.finishConnection(this);
             } else {
@@ -729,6 +752,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
 
     private void rejectCallFromProposed() {
         transitionOrThrow(State.REJECTED);
+        writeLogMessageMissed();
         xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
         this.sendJingleMessage("reject");
         jingleConnectionManager.finishConnection(this);

src/main/res/values/strings.xml ๐Ÿ”—

@@ -907,6 +907,7 @@
     <string name="incoming_call_duration">Incoming call ยท %s</string>
     <string name="outgoing_call">Outgoing call</string>
     <string name="outgoing_call_duration">Outgoing call ยท %s</string>
+    <string name="missed_call">Missed call</string>
     <plurals name="view_users">
         <item quantity="one">View %1$d Participant</item>
         <item quantity="other">View %1$d Participants</item>