show call log messages in conversation stream

This commit is contained in:
Daniel Gultsch 2020-04-12 17:12:59 +02:00
parent 1dc88f38ca
commit 3439f40411
48 changed files with 1077 additions and 855 deletions

View file

@ -57,6 +57,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
public static final int TYPE_STATUS = 3; public static final int TYPE_STATUS = 3;
public static final int TYPE_PRIVATE = 4; public static final int TYPE_PRIVATE = 4;
public static final int TYPE_PRIVATE_FILE = 5; public static final int TYPE_PRIVATE_FILE = 5;
public static final int TYPE_RTP_SESSION = 6;
public static final String CONVERSATION = "conversationUuid"; public static final String CONVERSATION = "conversationUuid";
public static final String COUNTERPART = "counterpart"; public static final String COUNTERPART = "counterpart";
@ -151,6 +152,31 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
null); null);
} }
public Message(Conversation conversation, int status, int type, final String remoteMsgId) {
this(conversation, java.util.UUID.randomUUID().toString(),
conversation.getUuid(),
conversation.getJid() == null ? null : conversation.getJid().asBareJid(),
null,
null,
System.currentTimeMillis(),
Message.ENCRYPTION_NONE,
status,
type,
false,
remoteMsgId,
null,
null,
null,
true,
null,
false,
null,
null,
false,
false,
null);
}
protected Message(final Conversational conversation, final String uuid, final String conversationUUid, final Jid counterpart, protected Message(final Conversational conversation, final String uuid, final String conversationUUid, final Jid counterpart,
final Jid trueCounterpart, final String body, final long timeSent, final Jid trueCounterpart, final String body, final long timeSent,
final int encryption, final int status, final int type, final boolean carbon, final int encryption, final int status, final int type, final boolean carbon,

View file

@ -0,0 +1,59 @@
package eu.siacs.conversations.entities;
import android.support.annotation.DrawableRes;
import com.google.common.base.Strings;
import eu.siacs.conversations.R;
public class RtpSessionStatus {
public final boolean successful;
public final long duration;
public RtpSessionStatus(boolean successful, long duration) {
this.successful = successful;
this.duration = duration;
}
@Override
public String toString() {
return successful + ":" + duration;
}
public static RtpSessionStatus of(final String body) {
final String[] parts = Strings.nullToEmpty(body).split(":", 2);
long duration = 0;
if (parts.length == 2) {
try {
duration = Long.parseLong(parts[1]);
} catch (NumberFormatException e) {
//do nothing
}
}
boolean made;
try {
made = Boolean.parseBoolean(parts[0]);
} catch (Exception e) {
made = false;
}
return new RtpSessionStatus(made, duration);
}
public static @DrawableRes int getDrawable(final boolean received, final boolean successful, final boolean darkTheme) {
if (received) {
if (successful) {
return darkTheme ? R.drawable.ic_call_received_white_18dp : R.drawable.ic_call_received_black_18dp;
} else {
return darkTheme ? R.drawable.ic_call_missed_white_18dp : R.drawable.ic_call_missed_black_18dp;
}
} else {
if (successful) {
return darkTheme ? R.drawable.ic_call_made_white_18dp : R.drawable.ic_call_made_black_18dp;
} else {
return darkTheme ? R.drawable.ic_call_missed_outgoing_white_18dp : R.drawable.ic_call_missed_outgoing_black_18dp;
}
}
}
}

View file

@ -299,6 +299,8 @@ public class UIHelper {
return new Pair<>(context.getString(R.string.omemo_decryption_failed), true); return new Pair<>(context.getString(R.string.omemo_decryption_failed), true);
} else if (message.isFileOrImage()) { } else if (message.isFileOrImage()) {
return new Pair<>(getFileDescriptionString(context, message), true); 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);
} else { } else {
final String body = MessageUtils.filterLtrRtl(message.getBody()); final String body = MessageUtils.filterLtrRtl(message.getBody());
if (body.startsWith(Message.ME_COMMAND)) { if (body.startsWith(Message.ME_COMMAND)) {

View file

@ -1,5 +1,6 @@
package eu.siacs.conversations.xmpp.jingle; package eu.siacs.conversations.xmpp.jingle;
import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import com.google.common.base.Strings; import com.google.common.base.Strings;
@ -18,6 +19,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.RtpSessionStatus;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.stanzas.Group; import eu.siacs.conversations.xmpp.jingle.stanzas.Group;
@ -94,13 +99,27 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this); private final WebRTCWrapper webRTCWrapper = new WebRTCWrapper(this);
private final ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>(); private final ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>();
private final Message message;
private State state = State.NULL; private State state = State.NULL;
private RtpContentMap initiatorRtpContentMap; private RtpContentMap initiatorRtpContentMap;
private RtpContentMap responderRtpContentMap; private RtpContentMap responderRtpContentMap;
private long rtpConnectionStarted = 0; //time of 'connected'
JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) { JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
super(jingleConnectionManager, id, initiator); super(jingleConnectionManager, id, initiator);
final Conversation conversation = jingleConnectionManager.getXmppConnectionService().findOrCreateConversation(
id.account,
id.with.asBareJid(),
false,
false
);
this.message = new Message(
conversation,
isInitiator() ? Message.STATUS_SEND : Message.STATUS_RECEIVED,
Message.TYPE_RTP_SESSION,
id.sessionId
);
} }
private static State reasonToState(Reason reason) { private static State reasonToState(Reason reason) {
@ -153,7 +172,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
return; return;
} }
webRTCWrapper.close(); webRTCWrapper.close();
transitionOrThrow(reasonToState(wrapper.reason)); final State target = reasonToState(wrapper.reason);
transitionOrThrow(target);
writeLogMessage(target);
if (previous == State.PROPOSED || previous == State.SESSION_INITIALIZED) { if (previous == State.PROPOSED || previous == State.SESSION_INITIALIZED) {
xmppConnectionService.getNotificationService().cancelIncomingCallNotification(); xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
} }
@ -455,7 +476,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
if (transition(State.RETRACTED)) { if (transition(State.RETRACTED)) {
xmppConnectionService.getNotificationService().cancelIncomingCallNotification(); xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": session with " + id.with + " has been retracted"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": session with " + id.with + " has been retracted");
//TODO create missed call notification/message writeLogMessageMissed();
jingleConnectionManager.finishConnection(this); jingleConnectionManager.finishConnection(this);
} else { } else {
Log.d(Config.LOGTAG, "ignoring retract because already in " + this.state); Log.d(Config.LOGTAG, "ignoring retract because already in " + this.state);
@ -509,6 +530,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void sendSessionTerminate(final Reason reason, final String text) { private void sendSessionTerminate(final Reason reason, final String text) {
final State target = reasonToState(reason); final State target = reasonToState(reason);
transitionOrThrow(target); transitionOrThrow(target);
writeLogMessage(target);
final JinglePacket jinglePacket = new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId); final JinglePacket jinglePacket = new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId);
jinglePacket.setReason(reason, text); jinglePacket.setReason(reason, text);
send(jinglePacket); send(jinglePacket);
@ -773,9 +795,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
public void onConnectionChange(final PeerConnection.PeerConnectionState newState) { public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed to " + newState); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed to " + newState);
updateEndUserState(); updateEndUserState();
if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
this.rtpConnectionStarted = SystemClock.elapsedRealtime();
}
if (newState == PeerConnection.PeerConnectionState.FAILED) { if (newState == PeerConnection.PeerConnectionState.FAILED) {
if (TERMINATED.contains(this.state)) { if (TERMINATED.contains(this.state)) {
Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": not sending session-terminate after connectivity error because session is already in state "+this.state); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": not sending session-terminate after connectivity error because session is already in state " + this.state);
return; return;
} }
sendSessionTerminate(Reason.CONNECTIVITY_ERROR); sendSessionTerminate(Reason.CONNECTIVITY_ERROR);
@ -850,6 +875,37 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
} }
private void writeLogMessage(final State state) {
final long started = this.rtpConnectionStarted;
long duration = started <= 0 ? 0 : SystemClock.elapsedRealtime() - started;
if (state == State.TERMINATED_SUCCESS || (state == State.TERMINATED_CONNECTIVITY_ERROR && duration > 0)) {
writeLogMessageSuccess(duration);
} else {
writeLogMessageMissed();
}
}
private void writeLogMessageSuccess(final long duration) {
this.message.setBody(new RtpSessionStatus(true, duration).toString());
this.writeMessage();
}
private void writeLogMessageMissed() {
this.message.setBody(new RtpSessionStatus(false,0).toString());
this.writeMessage();
}
private void writeMessage() {
final Conversational conversational = message.getConversation();
if (conversational instanceof Conversation) {
((Conversation) conversational).add(this.message);
xmppConnectionService.databaseBackend.createMessage(message);
xmppConnectionService.updateConversationUi();
} else {
throw new IllegalStateException("Somehow the conversation in a message was a stub");
}
}
public State getState() { public State getState() {
return this.state; return this.state;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

View file

@ -1,24 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingTop="5dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:paddingTop="5dp"> android:paddingBottom="5dp">
<LinearLayout <LinearLayout
android:id="@+id/message_box"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/date_bubble_white" android:layout_centerHorizontal="true"
android:id="@+id/message_box" android:background="@drawable/date_bubble_white">
android:layout_centerHorizontal="true">
<TextView <TextView
android:id="@+id/message_body"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary" android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary"
android:id="@+id/message_body" /> tools:text="Yesterday" />
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingTop="5dp"
android:paddingRight="8dp"
android:paddingBottom="5dp">
<LinearLayout
android:id="@+id/message_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="@drawable/date_bubble_white"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/indicator_received"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4sp"
android:layout_marginLeft="0sp"
tools:alpha="0.57"
tools:src="@drawable/ic_call_received_black_18dp" />
<TextView
android:id="@+id/message_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@string/incoming_call"
android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary" />
</LinearLayout>
</RelativeLayout>

View file

@ -903,6 +903,10 @@
<string name="hang_up">Hang up</string> <string name="hang_up">Hang up</string>
<string name="ongoing_call">Ongoing call</string> <string name="ongoing_call">Ongoing call</string>
<string name="disable_tor_to_make_call">Disable Tor to make calls</string> <string name="disable_tor_to_make_call">Disable Tor to make calls</string>
<string name="incoming_call">Incoming call</string>
<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>
<plurals name="view_users"> <plurals name="view_users">
<item quantity="one">View %1$d Participant</item> <item quantity="one">View %1$d Participant</item>
<item quantity="other">View %1$d Participants</item> <item quantity="other">View %1$d Participants</item>