ask for resource and use jingle direct init when JMI is not available. fixes #3751

This commit is contained in:
Daniel Gultsch 2020-05-30 14:56:12 +02:00
parent 8edfc61346
commit 637c208f55
8 changed files with 332 additions and 218 deletions

View file

@ -9,146 +9,164 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class Presences { public class Presences {
private final Hashtable<String, Presence> presences = new Hashtable<>(); private final Hashtable<String, Presence> presences = new Hashtable<>();
public List<Presence> getPresences() { private static String nameWithoutVersion(String name) {
synchronized (this.presences) { String[] parts = name.split(" ");
return new ArrayList<>(this.presences.values()); if (parts.length > 1 && Character.isDigit(parts[parts.length - 1].charAt(0))) {
} StringBuilder output = new StringBuilder();
} for (int i = 0; i < parts.length - 1; ++i) {
if (output.length() != 0) {
output.append(' ');
}
output.append(parts[i]);
}
return output.toString();
} else {
return name;
}
}
public Presence get(String resource) { public List<Presence> getPresences() {
synchronized (this.presences) { synchronized (this.presences) {
return this.presences.get(resource); return new ArrayList<>(this.presences.values());
} }
} }
public void updatePresence(String resource, Presence presence) { public Map<String, Presence> getPresencesMap() {
synchronized (this.presences) { synchronized (this.presences) {
this.presences.put(resource, presence); return new HashMap<>(this.presences);
} }
} }
public void removePresence(String resource) { public Presence get(String resource) {
synchronized (this.presences) { synchronized (this.presences) {
this.presences.remove(resource); return this.presences.get(resource);
} }
} }
public void clearPresences() { public void updatePresence(String resource, Presence presence) {
synchronized (this.presences) { synchronized (this.presences) {
this.presences.clear(); this.presences.put(resource, presence);
} }
} }
public Presence.Status getShownStatus() { public void removePresence(String resource) {
Presence.Status status = Presence.Status.OFFLINE; synchronized (this.presences) {
synchronized (this.presences) { this.presences.remove(resource);
for(Presence p : presences.values()) { }
if (p.getStatus() == Presence.Status.DND) { }
return p.getStatus();
} else if (p.getStatus().compareTo(status) < 0){
status = p.getStatus();
}
}
}
return status;
}
public int size() { public void clearPresences() {
synchronized (this.presences) { synchronized (this.presences) {
return presences.size(); this.presences.clear();
} }
} }
public String[] toResourceArray() { public Presence.Status getShownStatus() {
synchronized (this.presences) { Presence.Status status = Presence.Status.OFFLINE;
final String[] presencesArray = new String[presences.size()]; synchronized (this.presences) {
presences.keySet().toArray(presencesArray); for (Presence p : presences.values()) {
return presencesArray; if (p.getStatus() == Presence.Status.DND) {
} return p.getStatus();
} } else if (p.getStatus().compareTo(status) < 0) {
status = p.getStatus();
}
}
}
return status;
}
public List<PresenceTemplate> asTemplates() { public int size() {
synchronized (this.presences) { synchronized (this.presences) {
ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size()); return presences.size();
for(Presence p : presences.values()) { }
if (p.getMessage() != null && !p.getMessage().trim().isEmpty()) { }
templates.add(new PresenceTemplate(p.getStatus(), p.getMessage()));
}
}
return templates;
}
}
public boolean has(String presence) { public String[] toResourceArray() {
synchronized (this.presences) { synchronized (this.presences) {
return presences.containsKey(presence); final String[] presencesArray = new String[presences.size()];
} presences.keySet().toArray(presencesArray);
} return presencesArray;
}
}
public List<String> getStatusMessages() { public List<PresenceTemplate> asTemplates() {
ArrayList<String> messages = new ArrayList<>(); synchronized (this.presences) {
synchronized (this.presences) { ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
for(Presence presence : this.presences.values()) { for (Presence p : presences.values()) {
String message = presence.getMessage() == null ? null : presence.getMessage().trim(); if (p.getMessage() != null && !p.getMessage().trim().isEmpty()) {
if (message != null && !message.isEmpty() && !messages.contains(message)) { templates.add(new PresenceTemplate(p.getStatus(), p.getMessage()));
messages.add(message); }
} }
} return templates;
} }
return messages; }
}
public boolean allOrNonSupport(String namespace) { public boolean has(String presence) {
synchronized (this.presences) { synchronized (this.presences) {
for(Presence presence : this.presences.values()) { return presences.containsKey(presence);
ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult(); }
if (disco == null || !disco.getFeatures().contains(namespace)) { }
return false;
}
}
}
return true;
}
public Pair<Map<String, String>,Map<String,String>> toTypeAndNameMap() { public List<String> getStatusMessages() {
Map<String,String> typeMap = new HashMap<>(); ArrayList<String> messages = new ArrayList<>();
Map<String,String> nameMap = new HashMap<>(); synchronized (this.presences) {
synchronized (this.presences) { for (Presence presence : this.presences.values()) {
for(Map.Entry<String,Presence> presenceEntry : this.presences.entrySet()) { String message = presence.getMessage() == null ? null : presence.getMessage().trim();
String resource = presenceEntry.getKey(); if (message != null && !message.isEmpty() && !messages.contains(message)) {
Presence presence = presenceEntry.getValue(); messages.add(message);
ServiceDiscoveryResult serviceDiscoveryResult = presence == null ? null : presence.getServiceDiscoveryResult(); }
if (serviceDiscoveryResult != null && serviceDiscoveryResult.getIdentities().size() > 0) { }
ServiceDiscoveryResult.Identity identity = serviceDiscoveryResult.getIdentities().get(0); }
String type = identity.getType(); return messages;
String name = identity.getName(); }
if (type != null) {
typeMap.put(resource,type);
}
if (name != null) {
nameMap.put(resource, nameWithoutVersion(name));
}
}
}
}
return new Pair<>(typeMap,nameMap);
}
private static String nameWithoutVersion(String name) { public boolean allOrNonSupport(String namespace) {
String[] parts = name.split(" "); synchronized (this.presences) {
if (parts.length > 1 && Character.isDigit(parts[parts.length -1].charAt(0))) { for (Presence presence : this.presences.values()) {
StringBuilder output = new StringBuilder(); ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
for(int i = 0; i < parts.length -1; ++i) { if (disco == null || !disco.getFeatures().contains(namespace)) {
if (output.length() != 0) { return false;
output.append(' '); }
} }
output.append(parts[i]); }
} return true;
return output.toString(); }
} else {
return name; public boolean anySupport(final String namespace) {
} synchronized (this.presences) {
} for (Presence presence : this.presences.values()) {
ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
if (disco != null && disco.getFeatures().contains(namespace)) {
return true;
}
}
}
return false;
}
public Pair<Map<String, String>, Map<String, String>> toTypeAndNameMap() {
Map<String, String> typeMap = new HashMap<>();
Map<String, String> nameMap = new HashMap<>();
synchronized (this.presences) {
for (Map.Entry<String, Presence> presenceEntry : this.presences.entrySet()) {
String resource = presenceEntry.getKey();
Presence presence = presenceEntry.getValue();
ServiceDiscoveryResult serviceDiscoveryResult = presence == null ? null : presence.getServiceDiscoveryResult();
if (serviceDiscoveryResult != null && serviceDiscoveryResult.getIdentities().size() > 0) {
ServiceDiscoveryResult.Identity identity = serviceDiscoveryResult.getIdentities().get(0);
String type = identity.getType();
String name = identity.getName();
if (type != null) {
typeMap.put(resource, type);
}
if (name != null) {
nameMap.put(resource, nameWithoutVersion(name));
}
}
}
}
return new Pair<>(typeMap, nameMap);
}
} }

View file

@ -116,6 +116,7 @@ import eu.siacs.conversations.utils.QuickLoader;
import eu.siacs.conversations.utils.StylingHelper; import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.TimeFrameUtils; import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
@ -1342,11 +1343,28 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
Toast.makeText(getActivity(), R.string.only_one_call_at_a_time, Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), R.string.only_one_call_at_a_time, Toast.LENGTH_LONG).show();
return; return;
} }
final Contact contact = conversation.getContact(); final Contact contact = conversation.getContact();
if (contact.getPresences().anySupport(Namespace.JINGLE_MESSAGE)) {
triggerRtpSession(contact.getAccount(),contact.getJid().asBareJid(),action);
} else {
final RtpCapability.Capability capability;
if (action.equals(RtpSessionActivity.ACTION_MAKE_VIDEO_CALL)) {
capability = RtpCapability.Capability.VIDEO;
} else {
capability = RtpCapability.Capability.AUDIO;
}
PresenceSelector.selectFullJidForDirectRtpConnection(activity, contact, capability, fullJid -> {
triggerRtpSession(contact.getAccount(), fullJid, action);
});
}
}
private void triggerRtpSession(final Account account, final Jid with, final String action) {
final Intent intent = new Intent(activity, RtpSessionActivity.class); final Intent intent = new Intent(activity, RtpSessionActivity.class);
intent.setAction(action); intent.setAction(action);
intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, contact.getAccount().getJid().toEscapedString()); intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString());
intent.putExtra(RtpSessionActivity.EXTRA_WITH, contact.getJid().asBareJid().toEscapedString()); intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toEscapedString());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent); startActivity(intent);

View file

@ -51,6 +51,7 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.MainThreadExecutor; import eu.siacs.conversations.ui.util.MainThreadExecutor;
import eu.siacs.conversations.utils.PermissionUtils; import eu.siacs.conversations.utils.PermissionUtils;
import eu.siacs.conversations.utils.TimeFrameUtils; import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.Media;
@ -306,7 +307,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) { private void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) {
checkMicrophoneAvailability(); checkMicrophoneAvailability();
xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with, media); if (with.isBareJid()) {
xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with, media);
} else {
final String sessionId = xmppConnectionService.getJingleConnectionManager().initializeRtpSession(account, with, media);
initializeActivityWithRunningRtpSession(account, with, sessionId);
resetIntent(account, with, sessionId);
}
putScreenInCallMode(media); putScreenInCallMode(media);
} }
@ -444,8 +451,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return false; return false;
} }
private void reInitializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) { private void reInitializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
runOnUiThread(() -> initializeActivityWithRunningRtpSession(account, with, sessionId)); runOnUiThread(() -> initializeActivityWithRunningRtpSession(account, with, sessionId));
resetIntent(account, with, sessionId);
}
private void resetIntent(final Account account, final Jid with, final String sessionId) {
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString()); intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
intent.putExtra(EXTRA_WITH, with.toEscapedString()); intent.putExtra(EXTRA_WITH, with.toEscapedString());
@ -838,7 +849,6 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
} }
private void retry(View view) { private void retry(View view) {
Log.d(Config.LOGTAG, "attempting retry");
final Intent intent = getIntent(); final Intent intent = getIntent();
final Account account = extractAccount(intent); final Account account = extractAccount(intent);
final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH)); final Jid with = Jid.ofEscaped(intent.getStringExtra(EXTRA_WITH));
@ -846,6 +856,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
final String action = intent.getAction(); final String action = intent.getAction();
final Set<Media> media = actionToMedia(lastAction == null ? action : lastAction); final Set<Media> media = actionToMedia(lastAction == null ? action : lastAction);
this.rtpConnectionReference = null; this.rtpConnectionReference = null;
Log.d(Config.LOGTAG, "attempting retry with " + with.toEscapedString());
proposeJingleRtpSession(account, with, media); proposeJingleRtpSession(account, with, media);
} }
@ -899,7 +910,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
return; return;
} }
//this happens when going from proposed session to actual session //this happens when going from proposed session to actual session
reInitializeActivityWithRunningRapSession(account, with, sessionId); reInitializeActivityWithRunningRtpSession(account, with, sessionId);
return; return;
} }
final AbstractJingleConnection.Id id = requireRtpConnection().getId(); final AbstractJingleConnection.Id id = requireRtpConnection().getId();
@ -976,8 +987,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
private void resetIntent(final Account account, Jid with, final RtpEndUserState state, final Set<Media> media) { private void resetIntent(final Account account, Jid with, final RtpEndUserState state, final Set<Media> media) {
final Intent intent = new Intent(Intent.ACTION_VIEW); final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(EXTRA_WITH, with.asBareJid().toEscapedString());
intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString()); intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
if (account.getRoster().getContact(with).getPresences().anySupport(Namespace.JINGLE_MESSAGE)) {
intent.putExtra(EXTRA_WITH, with.asBareJid().toEscapedString());
} else {
intent.putExtra(EXTRA_WITH, with.toEscapedString());
}
intent.putExtra(EXTRA_LAST_REPORTED_STATE, state.toString()); intent.putExtra(EXTRA_LAST_REPORTED_STATE, state.toString());
intent.putExtra(EXTRA_LAST_ACTION, media.contains(Media.VIDEO) ? ACTION_MAKE_VIDEO_CALL : ACTION_MAKE_VOICE_CALL); intent.putExtra(EXTRA_LAST_ACTION, media.contains(Media.VIDEO) ? ACTION_MAKE_VIDEO_CALL : ACTION_MAKE_VOICE_CALL);
setIntent(intent); setIntent(intent);

View file

@ -44,92 +44,110 @@ import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
public class PresenceSelector { public class PresenceSelector {
public static void showPresenceSelectionDialog(Activity activity, final Conversation conversation, final OnPresenceSelected listener) { public static void showPresenceSelectionDialog(Activity activity, final Conversation conversation, final OnPresenceSelected listener) {
final Contact contact = conversation.getContact(); final Contact contact = conversation.getContact();
final Presences presences = contact.getPresences(); final String[] resourceArray = contact.getPresences().toResourceArray();
AlertDialog.Builder builder = new AlertDialog.Builder(activity); showPresenceSelectionDialog(activity, contact, resourceArray, fullJid -> {
builder.setTitle(activity.getString(R.string.choose_presence)); conversation.setNextCounterpart(fullJid);
final String[] resourceArray = presences.toResourceArray(); listener.onPresenceSelected();
Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap(); });
final Map<String, String> resourceTypeMap = typeAndName.first; }
final Map<String, String> resourceNameMap = typeAndName.second;
final String[] readableIdentities = new String[resourceArray.length];
final AtomicInteger selectedResource = new AtomicInteger(0);
for (int i = 0; i < resourceArray.length; ++i) {
String resource = resourceArray[i];
if (resource.equals(contact.getLastResource())) {
selectedResource.set(i);
}
String type = resourceTypeMap.get(resource);
String name = resourceNameMap.get(resource);
if (type != null) {
if (Collections.frequency(resourceTypeMap.values(), type) == 1) {
readableIdentities[i] = translateType(activity, type);
} else if (name != null) {
if (Collections.frequency(resourceNameMap.values(), name) == 1
|| CryptoHelper.UUID_PATTERN.matcher(resource).matches()) {
readableIdentities[i] = translateType(activity, type) + " (" + name + ")";
} else {
readableIdentities[i] = translateType(activity, type) + " (" + name + " / " + resource + ")";
}
} else {
readableIdentities[i] = translateType(activity, type) + " (" + resource + ")";
}
} else {
readableIdentities[i] = resource;
}
}
builder.setSingleChoiceItems(readableIdentities,
selectedResource.get(),
(dialog, which) -> selectedResource.set(which));
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.ok, (dialog, which) -> {
try {
Jid next = Jid.of(contact.getJid().getLocal(), contact.getJid().getDomain(), resourceArray[selectedResource.get()]);
conversation.setNextCounterpart(next);
} catch (IllegalArgumentException e) {
conversation.setNextCounterpart(null);
}
listener.onPresenceSelected();
});
builder.create().show();
}
public static void warnMutualPresenceSubscription(Activity activity, final Conversation conversation, final OnPresenceSelected listener) { public static void selectFullJidForDirectRtpConnection(final Activity activity, final Contact contact, final RtpCapability.Capability required, final OnFullJidSelected onFullJidSelected) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity); final String[] resources = RtpCapability.filterPresences(contact, required);
builder.setTitle(conversation.getContact().getJid().toString()); if (resources.length == 1) {
builder.setMessage(R.string.without_mutual_presence_updates); onFullJidSelected.onFullJidSelected(contact.getJid().withResource(resources[0]));
builder.setNegativeButton(R.string.cancel, null); } else {
builder.setPositiveButton(R.string.ignore, (dialog, which) -> { showPresenceSelectionDialog(activity, contact, resources, onFullJidSelected);
conversation.setNextCounterpart(null); }
if (listener != null) { }
listener.onPresenceSelected();
}
});
builder.create().show();
}
private static String translateType(Context context, String type) { private static void showPresenceSelectionDialog(final Activity activity, final Contact contact, final String[] resourceArray, final OnFullJidSelected onFullJidSelected) {
switch (type.toLowerCase()) { final Presences presences = contact.getPresences();
case "pc": AlertDialog.Builder builder = new AlertDialog.Builder(activity);
return context.getString(R.string.type_pc); builder.setTitle(activity.getString(R.string.choose_presence));
case "phone": Pair<Map<String, String>, Map<String, String>> typeAndName = presences.toTypeAndNameMap();
return context.getString(R.string.type_phone); final Map<String, String> resourceTypeMap = typeAndName.first;
case "tablet": final Map<String, String> resourceNameMap = typeAndName.second;
return context.getString(R.string.type_tablet); final String[] readableIdentities = new String[resourceArray.length];
case "web": final AtomicInteger selectedResource = new AtomicInteger(0);
return context.getString(R.string.type_web); for (int i = 0; i < resourceArray.length; ++i) {
case "console": String resource = resourceArray[i];
return context.getString(R.string.type_console); if (resource.equals(contact.getLastResource())) {
default: selectedResource.set(i);
return type; }
} String type = resourceTypeMap.get(resource);
} String name = resourceNameMap.get(resource);
if (type != null) {
if (Collections.frequency(resourceTypeMap.values(), type) == 1) {
readableIdentities[i] = translateType(activity, type);
} else if (name != null) {
if (Collections.frequency(resourceNameMap.values(), name) == 1
|| CryptoHelper.UUID_PATTERN.matcher(resource).matches()) {
readableIdentities[i] = translateType(activity, type) + " (" + name + ")";
} else {
readableIdentities[i] = translateType(activity, type) + " (" + name + " / " + resource + ")";
}
} else {
readableIdentities[i] = translateType(activity, type) + " (" + resource + ")";
}
} else {
readableIdentities[i] = resource;
}
}
builder.setSingleChoiceItems(readableIdentities,
selectedResource.get(),
(dialog, which) -> selectedResource.set(which));
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(
R.string.ok,
(dialog, which) -> onFullJidSelected.onFullJidSelected(
Jid.of(contact.getJid().getLocal(), contact.getJid().getDomain(), resourceArray[selectedResource.get()])
)
);
builder.create().show();
}
public interface OnPresenceSelected { public static void warnMutualPresenceSubscription(Activity activity, final Conversation conversation, final OnPresenceSelected listener) {
void onPresenceSelected(); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
} builder.setTitle(conversation.getContact().getJid().toString());
builder.setMessage(R.string.without_mutual_presence_updates);
builder.setNegativeButton(R.string.cancel, null);
builder.setPositiveButton(R.string.ignore, (dialog, which) -> {
conversation.setNextCounterpart(null);
if (listener != null) {
listener.onPresenceSelected();
}
});
builder.create().show();
}
private static String translateType(Context context, String type) {
switch (type.toLowerCase()) {
case "pc":
return context.getString(R.string.type_pc);
case "phone":
return context.getString(R.string.type_phone);
case "tablet":
return context.getString(R.string.type_tablet);
case "web":
return context.getString(R.string.type_web);
case "console":
return context.getString(R.string.type_console);
default:
return type;
}
}
public interface OnPresenceSelected {
void onPresenceSelected();
}
public interface OnFullJidSelected {
void onFullJidSelected(Jid jid);
}
} }

View file

@ -62,6 +62,10 @@ public abstract class AbstractJingleConnection {
return new Id(account, with, sessionId); return new Id(account, with, sessionId);
} }
public static Id of(Account account, Jid with) {
return new Id(account, with, JingleConnectionManager.nextRandomId());
}
public static Id of(Message message) { public static Id of(Message message) {
return new Id( return new Id(
message.getConversation().getAccount(), message.getConversation().getAccount(),

View file

@ -11,6 +11,7 @@ import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.ComparisonChain; import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.j2objc.annotations.Weak;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.security.SecureRandom; import java.security.SecureRandom;
@ -523,6 +524,15 @@ public class JingleConnectionManager extends AbstractConnectionManager {
mXmppConnectionService.sendMessagePacket(account, messagePacket); mXmppConnectionService.sendMessagePacket(account, messagePacket);
} }
public String initializeRtpSession(final Account account, final Jid with, final Set<Media> media) {
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with);
final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, account.getJid());
rtpConnection.setProposedMedia(media);
this.connections.put(id, rtpConnection);
rtpConnection.sendSessionInitiate();
return id.sessionId;
}
public void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) { public void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) {
synchronized (this.rtpSessionProposals) { synchronized (this.rtpSessionProposals) {
for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) { for (Map.Entry<RtpSessionProposal, DeviceDiscoveryState> entry : this.rtpSessionProposals.entrySet()) {

View file

@ -640,6 +640,10 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
} }
public void sendSessionInitiate() {
sendSessionInitiate(this.proposedMedia, State.SESSION_INITIALIZED);
}
private void sendSessionInitiate(final Set<Media> media, final State targetState) { private void sendSessionInitiate(final Set<Media> media, final State targetState) {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate");
discoverIceServers(iceServers -> sendSessionInitiate(media, targetState, iceServers)); discoverIceServers(iceServers -> sendSessionInitiate(media, targetState, iceServers));
@ -781,6 +785,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
public RtpEndUserState getEndUserState() { public RtpEndUserState getEndUserState() {
switch (this.state) { switch (this.state) {
case NULL:
case PROPOSED: case PROPOSED:
case SESSION_INITIALIZED: case SESSION_INITIALIZED:
if (isInitiator()) { if (isInitiator()) {
@ -836,10 +841,19 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
public Set<Media> getMedia() { public Set<Media> getMedia() {
final State current = getState(); final State current = getState();
if (current == State.NULL) { if (current == State.NULL) {
if (isInitiator()) {
return Preconditions.checkNotNull(
this.proposedMedia,
"RTP connection has not been initialized properly"
);
}
throw new IllegalStateException("RTP connection has not been initialized yet"); throw new IllegalStateException("RTP connection has not been initialized yet");
} }
if (Arrays.asList(State.PROPOSED, State.PROCEED).contains(current)) { if (Arrays.asList(State.PROPOSED, State.PROCEED).contains(current)) {
return Preconditions.checkNotNull(this.proposedMedia, "RTP connection has not been initialized properly"); return Preconditions.checkNotNull(
this.proposedMedia,
"RTP connection has not been initialized properly"
);
} }
final RtpContentMap initiatorContentMap = initiatorRtpContentMap; final RtpContentMap initiatorContentMap = initiatorRtpContentMap;
if (initiatorContentMap != null) { if (initiatorContentMap != null) {

View file

@ -1,8 +1,10 @@
package eu.siacs.conversations.xmpp.jingle; package eu.siacs.conversations.xmpp.jingle;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Presence; import eu.siacs.conversations.entities.Presence;
@ -37,6 +39,21 @@ public class RtpCapability {
return Capability.NONE; return Capability.NONE;
} }
public static String[] filterPresences(final Contact contact, Capability required) {
final Presences presences = contact.getPresences();
final ArrayList<String> resources = new ArrayList<>();
for(final Map.Entry<String,Presence> presence : presences.getPresencesMap().entrySet()) {
final Capability capability = check(presence.getValue());
if (capability == Capability.NONE) {
continue;
}
if (required == Capability.AUDIO || capability == required) {
resources.add(presence.getKey());
}
}
return resources.toArray(new String[0]);
}
public static Capability check(final Contact contact) { public static Capability check(final Contact contact) {
final Presences presences = contact.getPresences(); final Presences presences = contact.getPresences();
Capability result = Capability.NONE; Capability result = Capability.NONE;