show call log messages in conversation stream
|
@ -57,6 +57,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
public static final int TYPE_STATUS = 3;
|
||||
public static final int TYPE_PRIVATE = 4;
|
||||
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 COUNTERPART = "counterpart";
|
||||
|
@ -151,6 +152,31 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
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,
|
||||
final Jid trueCounterpart, final String body, final long timeSent,
|
||||
final int encryption, final int status, final int type, final boolean carbon,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -299,6 +299,8 @@ public class UIHelper {
|
|||
return new Pair<>(context.getString(R.string.omemo_decryption_failed), true);
|
||||
} 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);
|
||||
} else {
|
||||
final String body = MessageUtils.filterLtrRtl(message.getBody());
|
||||
if (body.startsWith(Message.ME_COMMAND)) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package eu.siacs.conversations.xmpp.jingle;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
@ -18,6 +19,10 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
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.Namespace;
|
||||
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 ArrayDeque<IceCandidate> pendingIceCandidates = new ArrayDeque<>();
|
||||
private final Message message;
|
||||
private State state = State.NULL;
|
||||
private RtpContentMap initiatorRtpContentMap;
|
||||
private RtpContentMap responderRtpContentMap;
|
||||
private long rtpConnectionStarted = 0; //time of 'connected'
|
||||
|
||||
|
||||
JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid 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) {
|
||||
|
@ -153,7 +172,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
return;
|
||||
}
|
||||
webRTCWrapper.close();
|
||||
transitionOrThrow(reasonToState(wrapper.reason));
|
||||
final State target = reasonToState(wrapper.reason);
|
||||
transitionOrThrow(target);
|
||||
writeLogMessage(target);
|
||||
if (previous == State.PROPOSED || previous == State.SESSION_INITIALIZED) {
|
||||
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
|
||||
}
|
||||
|
@ -455,7 +476,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
if (transition(State.RETRACTED)) {
|
||||
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
|
||||
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);
|
||||
} else {
|
||||
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) {
|
||||
final State target = reasonToState(reason);
|
||||
transitionOrThrow(target);
|
||||
writeLogMessage(target);
|
||||
final JinglePacket jinglePacket = new JinglePacket(JinglePacket.Action.SESSION_TERMINATE, id.sessionId);
|
||||
jinglePacket.setReason(reason, text);
|
||||
send(jinglePacket);
|
||||
|
@ -773,9 +795,12 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
public void onConnectionChange(final PeerConnection.PeerConnectionState newState) {
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": PeerConnectionState changed to " + newState);
|
||||
updateEndUserState();
|
||||
if (newState == PeerConnection.PeerConnectionState.CONNECTED && this.rtpConnectionStarted == 0) {
|
||||
this.rtpConnectionStarted = SystemClock.elapsedRealtime();
|
||||
}
|
||||
if (newState == PeerConnection.PeerConnectionState.FAILED) {
|
||||
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;
|
||||
}
|
||||
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() {
|
||||
return this.state;
|
||||
}
|
||||
|
|
BIN
src/main/res/drawable-hdpi/ic_call_made_black_18dp.png
Normal file
After Width: | Height: | Size: 159 B |
BIN
src/main/res/drawable-hdpi/ic_call_made_white_18dp.png
Normal file
After Width: | Height: | Size: 174 B |
BIN
src/main/res/drawable-hdpi/ic_call_missed_black_18dp.png
Normal file
After Width: | Height: | Size: 179 B |
After Width: | Height: | Size: 180 B |
After Width: | Height: | Size: 180 B |
BIN
src/main/res/drawable-hdpi/ic_call_missed_white_18dp.png
Normal file
After Width: | Height: | Size: 191 B |
BIN
src/main/res/drawable-hdpi/ic_call_received_black_18dp.png
Normal file
After Width: | Height: | Size: 159 B |
BIN
src/main/res/drawable-hdpi/ic_call_received_white_18dp.png
Normal file
After Width: | Height: | Size: 169 B |
BIN
src/main/res/drawable-mdpi/ic_call_made_black_18dp.png
Normal file
After Width: | Height: | Size: 132 B |
BIN
src/main/res/drawable-mdpi/ic_call_made_white_18dp.png
Normal file
After Width: | Height: | Size: 135 B |
BIN
src/main/res/drawable-mdpi/ic_call_missed_black_18dp.png
Normal file
After Width: | Height: | Size: 141 B |
After Width: | Height: | Size: 134 B |
After Width: | Height: | Size: 136 B |
BIN
src/main/res/drawable-mdpi/ic_call_missed_white_18dp.png
Normal file
After Width: | Height: | Size: 147 B |
BIN
src/main/res/drawable-mdpi/ic_call_received_black_18dp.png
Normal file
After Width: | Height: | Size: 133 B |
BIN
src/main/res/drawable-mdpi/ic_call_received_white_18dp.png
Normal file
After Width: | Height: | Size: 140 B |
BIN
src/main/res/drawable-xhdpi/ic_call_made_black_18dp.png
Normal file
After Width: | Height: | Size: 174 B |
BIN
src/main/res/drawable-xhdpi/ic_call_made_white_18dp.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
src/main/res/drawable-xhdpi/ic_call_missed_black_18dp.png
Normal file
After Width: | Height: | Size: 201 B |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 193 B |
BIN
src/main/res/drawable-xhdpi/ic_call_missed_white_18dp.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
src/main/res/drawable-xhdpi/ic_call_received_black_18dp.png
Normal file
After Width: | Height: | Size: 175 B |
BIN
src/main/res/drawable-xhdpi/ic_call_received_white_18dp.png
Normal file
After Width: | Height: | Size: 189 B |
BIN
src/main/res/drawable-xxhdpi/ic_call_made_black_18dp.png
Normal file
After Width: | Height: | Size: 202 B |
BIN
src/main/res/drawable-xxhdpi/ic_call_made_white_18dp.png
Normal file
After Width: | Height: | Size: 225 B |
BIN
src/main/res/drawable-xxhdpi/ic_call_missed_black_18dp.png
Normal file
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 235 B |
After Width: | Height: | Size: 235 B |
BIN
src/main/res/drawable-xxhdpi/ic_call_missed_white_18dp.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
src/main/res/drawable-xxhdpi/ic_call_received_black_18dp.png
Normal file
After Width: | Height: | Size: 202 B |
BIN
src/main/res/drawable-xxhdpi/ic_call_received_white_18dp.png
Normal file
After Width: | Height: | Size: 228 B |
BIN
src/main/res/drawable-xxxhdpi/ic_call_made_black_18dp.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
src/main/res/drawable-xxxhdpi/ic_call_made_white_18dp.png
Normal file
After Width: | Height: | Size: 247 B |
BIN
src/main/res/drawable-xxxhdpi/ic_call_missed_black_18dp.png
Normal file
After Width: | Height: | Size: 267 B |
After Width: | Height: | Size: 257 B |
After Width: | Height: | Size: 248 B |
BIN
src/main/res/drawable-xxxhdpi/ic_call_missed_white_18dp.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
src/main/res/drawable-xxxhdpi/ic_call_received_black_18dp.png
Normal file
After Width: | Height: | Size: 214 B |
BIN
src/main/res/drawable-xxxhdpi/ic_call_received_white_18dp.png
Normal file
After Width: | Height: | Size: 257 B |
|
@ -1,24 +1,27 @@
|
|||
<?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:paddingBottom="5dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingTop="5dp">
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/message_box"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/date_bubble_white"
|
||||
android:id="@+id/message_box"
|
||||
android:layout_centerHorizontal="true">
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/date_bubble_white">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message_body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary"
|
||||
android:id="@+id/message_body" />
|
||||
tools:text="Yesterday" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
38
src/main/res/layout/message_rtp_session.xml
Normal 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>
|
|
@ -903,6 +903,10 @@
|
|||
<string name="hang_up">Hang up</string>
|
||||
<string name="ongoing_call">Ongoing call</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">
|
||||
<item quantity="one">View %1$d Participant</item>
|
||||
<item quantity="other">View %1$d Participants</item>
|
||||
|
|