ask for resource and use jingle direct init when JMI is not available. fixes #3751
This commit is contained in:
parent
8edfc61346
commit
637c208f55
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue