From 637c208f553a08814cd27ea4d4756b95da87ae52 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 30 May 2020 14:56:12 +0200 Subject: [PATCH] ask for resource and use jingle direct init when JMI is not available. fixes #3751 --- .../conversations/entities/Presences.java | 274 ++++++++++-------- .../ui/ConversationFragment.java | 22 +- .../conversations/ui/RtpSessionActivity.java | 25 +- .../ui/util/PresenceSelector.java | 182 ++++++------ .../xmpp/jingle/AbstractJingleConnection.java | 4 + .../xmpp/jingle/JingleConnectionManager.java | 10 + .../xmpp/jingle/JingleRtpConnection.java | 16 +- .../xmpp/jingle/RtpCapability.java | 17 ++ 8 files changed, 332 insertions(+), 218 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java index 5a8452019..feca98839 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presences.java +++ b/src/main/java/eu/siacs/conversations/entities/Presences.java @@ -9,146 +9,164 @@ import java.util.List; import java.util.Map; public class Presences { - private final Hashtable presences = new Hashtable<>(); + private final Hashtable presences = new Hashtable<>(); - public List getPresences() { - synchronized (this.presences) { - return new ArrayList<>(this.presences.values()); - } - } + private static String nameWithoutVersion(String name) { + String[] parts = name.split(" "); + 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) { - synchronized (this.presences) { - return this.presences.get(resource); - } - } + public List getPresences() { + synchronized (this.presences) { + return new ArrayList<>(this.presences.values()); + } + } - public void updatePresence(String resource, Presence presence) { - synchronized (this.presences) { - this.presences.put(resource, presence); - } - } + public Map getPresencesMap() { + synchronized (this.presences) { + return new HashMap<>(this.presences); + } + } - public void removePresence(String resource) { - synchronized (this.presences) { - this.presences.remove(resource); - } - } + public Presence get(String resource) { + synchronized (this.presences) { + return this.presences.get(resource); + } + } - public void clearPresences() { - synchronized (this.presences) { - this.presences.clear(); - } - } + public void updatePresence(String resource, Presence presence) { + synchronized (this.presences) { + this.presences.put(resource, presence); + } + } - public Presence.Status getShownStatus() { - Presence.Status status = Presence.Status.OFFLINE; - synchronized (this.presences) { - 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 void removePresence(String resource) { + synchronized (this.presences) { + this.presences.remove(resource); + } + } - public int size() { - synchronized (this.presences) { - return presences.size(); - } - } + public void clearPresences() { + synchronized (this.presences) { + this.presences.clear(); + } + } - public String[] toResourceArray() { - synchronized (this.presences) { - final String[] presencesArray = new String[presences.size()]; - presences.keySet().toArray(presencesArray); - return presencesArray; - } - } + public Presence.Status getShownStatus() { + Presence.Status status = Presence.Status.OFFLINE; + synchronized (this.presences) { + 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 List asTemplates() { - synchronized (this.presences) { - ArrayList templates = new ArrayList<>(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 int size() { + synchronized (this.presences) { + return presences.size(); + } + } - public boolean has(String presence) { - synchronized (this.presences) { - return presences.containsKey(presence); - } - } + public String[] toResourceArray() { + synchronized (this.presences) { + final String[] presencesArray = new String[presences.size()]; + presences.keySet().toArray(presencesArray); + return presencesArray; + } + } - public List getStatusMessages() { - ArrayList messages = new ArrayList<>(); - synchronized (this.presences) { - for(Presence presence : this.presences.values()) { - String message = presence.getMessage() == null ? null : presence.getMessage().trim(); - if (message != null && !message.isEmpty() && !messages.contains(message)) { - messages.add(message); - } - } - } - return messages; - } + public List asTemplates() { + synchronized (this.presences) { + ArrayList templates = new ArrayList<>(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 allOrNonSupport(String namespace) { - synchronized (this.presences) { - for(Presence presence : this.presences.values()) { - ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult(); - if (disco == null || !disco.getFeatures().contains(namespace)) { - return false; - } - } - } - return true; - } + public boolean has(String presence) { + synchronized (this.presences) { + return presences.containsKey(presence); + } + } - public Pair,Map> toTypeAndNameMap() { - Map typeMap = new HashMap<>(); - Map nameMap = new HashMap<>(); - synchronized (this.presences) { - for(Map.Entry 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); - } + public List getStatusMessages() { + ArrayList messages = new ArrayList<>(); + synchronized (this.presences) { + for (Presence presence : this.presences.values()) { + String message = presence.getMessage() == null ? null : presence.getMessage().trim(); + if (message != null && !message.isEmpty() && !messages.contains(message)) { + messages.add(message); + } + } + } + return messages; + } - private static String nameWithoutVersion(String name) { - String[] parts = name.split(" "); - 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 boolean allOrNonSupport(String namespace) { + synchronized (this.presences) { + for (Presence presence : this.presences.values()) { + ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult(); + if (disco == null || !disco.getFeatures().contains(namespace)) { + return false; + } + } + } + return true; + } + + 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> toTypeAndNameMap() { + Map typeMap = new HashMap<>(); + Map nameMap = new HashMap<>(); + synchronized (this.presences) { + for (Map.Entry 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); + } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index d60516cc6..329a8b984 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -116,6 +116,7 @@ import eu.siacs.conversations.utils.QuickLoader; import eu.siacs.conversations.utils.StylingHelper; import eu.siacs.conversations.utils.TimeFrameUtils; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.chatstate.ChatState; 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(); return; } + 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); intent.setAction(action); - intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, contact.getAccount().getJid().toEscapedString()); - intent.putExtra(RtpSessionActivity.EXTRA_WITH, contact.getJid().asBareJid().toEscapedString()); + intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.getJid().toEscapedString()); + intent.putExtra(RtpSessionActivity.EXTRA_WITH, with.toEscapedString()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 76522cee8..b7e018abf 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -51,6 +51,7 @@ import eu.siacs.conversations.ui.util.AvatarWorkerTask; import eu.siacs.conversations.ui.util.MainThreadExecutor; import eu.siacs.conversations.utils.PermissionUtils; 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.JingleRtpConnection; 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) { 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); } @@ -444,8 +451,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe 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)); + resetIntent(account, with, sessionId); + } + + private void resetIntent(final Account account, final Jid with, final String sessionId) { final Intent intent = new Intent(Intent.ACTION_VIEW); intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString()); intent.putExtra(EXTRA_WITH, with.toEscapedString()); @@ -838,7 +849,6 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe } private void retry(View view) { - Log.d(Config.LOGTAG, "attempting retry"); final Intent intent = getIntent(); final Account account = extractAccount(intent); 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 Set media = actionToMedia(lastAction == null ? action : lastAction); this.rtpConnectionReference = null; + Log.d(Config.LOGTAG, "attempting retry with " + with.toEscapedString()); proposeJingleRtpSession(account, with, media); } @@ -899,7 +910,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe return; } //this happens when going from proposed session to actual session - reInitializeActivityWithRunningRapSession(account, with, sessionId); + reInitializeActivityWithRunningRtpSession(account, with, sessionId); return; } 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) { final Intent intent = new Intent(Intent.ACTION_VIEW); - intent.putExtra(EXTRA_WITH, with.asBareJid().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_ACTION, media.contains(Media.VIDEO) ? ACTION_MAKE_VIDEO_CALL : ACTION_MAKE_VOICE_CALL); setIntent(intent); diff --git a/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java b/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java index defabcb4a..14e82b406 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java +++ b/src/main/java/eu/siacs/conversations/ui/util/PresenceSelector.java @@ -44,92 +44,110 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xmpp.Jid; +import eu.siacs.conversations.xmpp.jingle.RtpCapability; public class PresenceSelector { - public static void showPresenceSelectionDialog(Activity activity, final Conversation conversation, final OnPresenceSelected listener) { - final Contact contact = conversation.getContact(); - final Presences presences = contact.getPresences(); - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(activity.getString(R.string.choose_presence)); - final String[] resourceArray = presences.toResourceArray(); - Pair, Map> typeAndName = presences.toTypeAndNameMap(); - final Map resourceTypeMap = typeAndName.first; - final Map 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 showPresenceSelectionDialog(Activity activity, final Conversation conversation, final OnPresenceSelected listener) { + final Contact contact = conversation.getContact(); + final String[] resourceArray = contact.getPresences().toResourceArray(); + showPresenceSelectionDialog(activity, contact, resourceArray, fullJid -> { + conversation.setNextCounterpart(fullJid); + listener.onPresenceSelected(); + }); + } - public static void warnMutualPresenceSubscription(Activity activity, final Conversation conversation, final OnPresenceSelected listener) { - 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(); - } + public static void selectFullJidForDirectRtpConnection(final Activity activity, final Contact contact, final RtpCapability.Capability required, final OnFullJidSelected onFullJidSelected) { + final String[] resources = RtpCapability.filterPresences(contact, required); + if (resources.length == 1) { + onFullJidSelected.onFullJidSelected(contact.getJid().withResource(resources[0])); + } else { + showPresenceSelectionDialog(activity, contact, resources, onFullJidSelected); + } + } - 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; - } - } + private static void showPresenceSelectionDialog(final Activity activity, final Contact contact, final String[] resourceArray, final OnFullJidSelected onFullJidSelected) { + final Presences presences = contact.getPresences(); + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(activity.getString(R.string.choose_presence)); + Pair, Map> typeAndName = presences.toTypeAndNameMap(); + final Map resourceTypeMap = typeAndName.first; + final Map 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) -> onFullJidSelected.onFullJidSelected( + Jid.of(contact.getJid().getLocal(), contact.getJid().getDomain(), resourceArray[selectedResource.get()]) + ) + ); + builder.create().show(); + } - public interface OnPresenceSelected { - void onPresenceSelected(); - } + public static void warnMutualPresenceSubscription(Activity activity, final Conversation conversation, final OnPresenceSelected listener) { + 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); + } } 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 d95019048..679a30abe 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java @@ -62,6 +62,10 @@ public abstract class AbstractJingleConnection { 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) { return new Id( message.getConversation().getAccount(), 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 0e161bd5a..4a1f7148f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -11,6 +11,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.Collections2; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableSet; +import com.google.j2objc.annotations.Weak; import java.lang.ref.WeakReference; import java.security.SecureRandom; @@ -523,6 +524,15 @@ public class JingleConnectionManager extends AbstractConnectionManager { mXmppConnectionService.sendMessagePacket(account, messagePacket); } + public String initializeRtpSession(final Account account, final Jid with, final Set 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) { synchronized (this.rtpSessionProposals) { for (Map.Entry entry : this.rtpSessionProposals.entrySet()) { 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 0704f695a..8ff496835 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -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, final State targetState) { Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": prepare session-initiate"); discoverIceServers(iceServers -> sendSessionInitiate(media, targetState, iceServers)); @@ -781,6 +785,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web public RtpEndUserState getEndUserState() { switch (this.state) { + case NULL: case PROPOSED: case SESSION_INITIALIZED: if (isInitiator()) { @@ -836,10 +841,19 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web public Set getMedia() { final State current = getState(); 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"); } 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; if (initiatorContentMap != null) { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java index d040694c4..7fad0459a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java @@ -1,8 +1,10 @@ package eu.siacs.conversations.xmpp.jingle; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Presence; @@ -37,6 +39,21 @@ public class RtpCapability { return Capability.NONE; } + public static String[] filterPresences(final Contact contact, Capability required) { + final Presences presences = contact.getPresences(); + final ArrayList resources = new ArrayList<>(); + for(final Map.Entry 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) { final Presences presences = contact.getPresences(); Capability result = Capability.NONE;