diff --git a/build.gradle b/build.gradle
index 6d83ddceaafb700f52c193c8529665e94a805774..c7ae50fd14151c22035d98466da5d76875cd8e76 100644
--- a/build.gradle
+++ b/build.gradle
@@ -130,6 +130,9 @@ dependencies {
implementation 'io.github.nishkarsh:android-permissions:2.1.6'
testImplementation 'junit:junit:4.13.2'
+ testImplementation 'org.robolectric:robolectric:4.14.1'
+ androidTestImplementation 'androidx.test.ext:junit:1.2.1'
+
}
ext {
diff --git a/fastlane/metadata/android/gl-ES/changelogs/4214204.txt b/fastlane/metadata/android/gl-ES/changelogs/4214204.txt
new file mode 100644
index 0000000000000000000000000000000000000000..527ce685ae76bed83856a020febd6ea6a22438d0
--- /dev/null
+++ b/fastlane/metadata/android/gl-ES/changelogs/4214204.txt
@@ -0,0 +1,2 @@
+* compatibilidade con 'Service Outage Status'
+* arranxo de problemas menores de seguridade ao procesar varios corpos da mensaxe, occupant-ids e stanza-id
diff --git a/fastlane/metadata/android/pt-BR/changelogs/379.txt b/fastlane/metadata/android/pt-BR/changelogs/379.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6429a13141e1f846935cb33888a10a3fb554a84e
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/379.txt
@@ -0,0 +1 @@
+Chamadas de Áudio/Vídeo (Requer suporte do servidor na forma de servidores STUN e TURN descobertos via XEP-0215)
diff --git a/fastlane/metadata/android/pt-BR/changelogs/381.txt b/fastlane/metadata/android/pt-BR/changelogs/381.txt
new file mode 100644
index 0000000000000000000000000000000000000000..049b99e4e62a5f11fde538d455168bca91023d1c
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/381.txt
@@ -0,0 +1,2 @@
+* Feedback audível (discando, chamada iniciada, chamada encerrada) para chamadas de voz.
+* Correção de problema com a tentativa de reintentar chamadas de vídeo falhadas
diff --git a/fastlane/metadata/android/pt-BR/changelogs/42062.txt b/fastlane/metadata/android/pt-BR/changelogs/42062.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d239746242890bb4b29b0bfc25f7305c388edbd5
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/42062.txt
@@ -0,0 +1 @@
+* Desabilitar a abertura de arquivos de backup (.ceb) pelo gerenciador de arquivos
diff --git a/fastlane/metadata/android/pt-BR/changelogs/42065.txt b/fastlane/metadata/android/pt-BR/changelogs/42065.txt
new file mode 100644
index 0000000000000000000000000000000000000000..50d69c942f7b14c2f193bd3d02a95d4416c2ee7c
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/42065.txt
@@ -0,0 +1 @@
+* Introduzir novo formato de arquivo de backup
diff --git a/fastlane/metadata/android/pt-BR/changelogs/4211604.txt b/fastlane/metadata/android/pt-BR/changelogs/4211604.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8df2f636760626284ec2ff2b269ed82f095d58a1
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/4211604.txt
@@ -0,0 +1 @@
+* Correções de bugs menores
diff --git a/fastlane/metadata/android/pt-BR/changelogs/4211804.txt b/fastlane/metadata/android/pt-BR/changelogs/4211804.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6585028bdf1002ee96de2283564cd0a328a023fe
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/4211804.txt
@@ -0,0 +1 @@
+* Adicionar tempo limite para a iniciação da chamada
diff --git a/fastlane/metadata/android/pt-BR/changelogs/4212104.txt b/fastlane/metadata/android/pt-BR/changelogs/4212104.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ae2419d89ac810372e91029ee045ad5cedd69282
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/4212104.txt
@@ -0,0 +1 @@
+* Suporte a Reações de Mensagens
diff --git a/fastlane/metadata/android/pt-BR/changelogs/4212504.txt b/fastlane/metadata/android/pt-BR/changelogs/4212504.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6aec1d05f42e06866be479401c3b54e7c398c25f
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/4212504.txt
@@ -0,0 +1 @@
+* Melhorar o manuseio de algumas reações com emojis
diff --git a/fastlane/metadata/android/pt-BR/changelogs/4212704.txt b/fastlane/metadata/android/pt-BR/changelogs/4212704.txt
new file mode 100644
index 0000000000000000000000000000000000000000..91499dcccdbeb984912eb5720675c0df64a40a90
--- /dev/null
+++ b/fastlane/metadata/android/pt-BR/changelogs/4212704.txt
@@ -0,0 +1 @@
+* Adicionar a capacidade de exibir as bolhas de mensagem alinhadas à esquerda
diff --git a/fastlane/metadata/android/uk/changelogs/4211804.txt b/fastlane/metadata/android/uk/changelogs/4211804.txt
index c3490793c919058ab1c1a700cb0fde390e090862..ee26cc2398c2408084f544b35c0d4decd23222e3 100644
--- a/fastlane/metadata/android/uk/changelogs/4211804.txt
+++ b/fastlane/metadata/android/uk/changelogs/4211804.txt
@@ -1 +1 @@
-* Додано таймаут для ініціювання виклику
+* Додано тайм-аут для ініціювання виклику
diff --git a/src/conversations/res/values-ga/strings.xml b/src/conversations/res/values-ga/strings.xml
index ee21d93fd943365566a149cdeee1e54576395699..91c996957b972fcfbb68429eb76993faf1f03c0d 100644
--- a/src/conversations/res/values-ga/strings.xml
+++ b/src/conversations/res/values-ga/strings.xml
@@ -3,4 +3,5 @@
Roghnaigh do freastalaí XMPP
Bain úsáid as conversations.im
Oscail cuntas nua
+ An bhfuil cuntas XMPP agat?
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 8866550a62c5099a78e39a355ae6f5c93ff06c17..28c3b0075b991e88628c1e1977545c468288507a 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -46,6 +46,17 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
+import eu.siacs.conversations.xmpp.manager.DiscoManager;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import org.json.JSONException;
+import org.json.JSONObject;
public class Account extends AbstractEntity implements AvatarService.Avatarable {
@@ -111,7 +122,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
private long mEndGracePeriod = 0L;
private final Map bookmarks = new HashMap<>();
private boolean bookmarksLoaded = false;
- private Presence.Status presenceStatus;
+ private im.conversations.android.xmpp.model.stanza.Presence.Availability presenceStatus;
private String presenceStatusMessage;
private String pinnedMechanism;
private String pinnedChannelBinding;
@@ -134,7 +145,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
null,
null,
Resolver.XMPP_PORT_STARTTLS,
- Presence.Status.ONLINE,
+ im.conversations.android.xmpp.model.stanza.Presence.Availability.ONLINE,
null,
null,
null,
@@ -153,7 +164,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
String displayName,
String hostname,
int port,
- final Presence.Status status,
+ final im.conversations.android.xmpp.model.stanza.Presence.Availability status,
String statusMessage,
final String pinnedMechanism,
final String pinnedChannelBinding,
@@ -216,7 +227,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
cursor.getString(cursor.getColumnIndexOrThrow(DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(HOSTNAME)),
cursor.getInt(cursor.getColumnIndexOrThrow(PORT)),
- Presence.Status.fromShowString(
+ im.conversations.android.xmpp.model.stanza.Presence.Availability.valueOfShown(
cursor.getString(cursor.getColumnIndexOrThrow(STATUS))),
cursor.getString(cursor.getColumnIndexOrThrow(STATUS_MESSAGE)),
cursor.getString(cursor.getColumnIndexOrThrow(PINNED_MECHANISM)),
@@ -489,11 +500,12 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
&& getXmppConnection().getAttempt() >= 3;
}
- public Presence.Status getPresenceStatus() {
+ public im.conversations.android.xmpp.model.stanza.Presence.Availability getPresenceStatus() {
return this.presenceStatus;
}
- public void setPresenceStatus(Presence.Status status) {
+ public void setPresenceStatus(
+ im.conversations.android.xmpp.model.stanza.Presence.Availability status) {
this.presenceStatus = status;
}
@@ -622,9 +634,18 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
}
public int activeDevicesWithRtpCapability() {
+ final var connection = getXmppConnection();
+ if (connection == null) {
+ return 0;
+ }
int i = 0;
- for (Presence presence : getSelfContact().getPresences().getPresences()) {
- if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
+ for (String resource : getSelfContact().getPresences().getPresencesMap().keySet()) {
+ final var jid =
+ Strings.isNullOrEmpty(resource)
+ ? getJid().asBareJid()
+ : getJid().withResource(resource);
+ if (RtpCapability.check(connection.getManager(DiscoManager.class).get(jid))
+ != RtpCapability.Capability.NONE) {
i++;
}
}
@@ -679,12 +700,15 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
}
public void refreshCapsFor(Contact contact) {
+ final var connection = getXmppConnection();
+ if (connection == null) return;
+
synchronized (gateways) {
for (final var k : new HashSet<>(gateways.keySet())) {
gateways.remove(k, contact);
}
- for (final var p : contact.getPresences().getPresences()) {
- final var disco = p.getServiceDiscoveryResult();
+ for (final var jid : contact.getPresences().getFullJids()) {
+ final var disco = connection.getManager(DiscoManager.class).get(jid);
if (disco == null) continue;
for (final var identity : disco.getIdentities()) {
if ("gateway".equals(identity.getCategory())) {
diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java
index 7520ed2b5ae341bcaf4919a801d579d0c8ac5d7a..4e7d351e61c2a7a009d96305f85f5ecfefc895cb 100644
--- a/src/main/java/eu/siacs/conversations/entities/Contact.java
+++ b/src/main/java/eu/siacs/conversations/entities/Contact.java
@@ -22,6 +22,17 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.android.AbstractPhoneContact;
+import eu.siacs.conversations.android.JabberIdContact;
+import eu.siacs.conversations.services.QuickConversationsService;
+import eu.siacs.conversations.utils.JidHelper;
+import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.Jid;
+import eu.siacs.conversations.xmpp.jingle.RtpCapability;
+import eu.siacs.conversations.xmpp.pep.Avatar;
+import im.conversations.android.xmpp.model.stanza.Presence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -73,7 +84,7 @@ public class Contact implements ListItem, Blockable {
private final JSONObject keys;
private JSONArray groups = new JSONArray();
private JSONArray systemTags = new JSONArray();
- private final Presences presences = new Presences();
+ private final Presences presences = new Presences(this);
protected Account account;
protected Avatar avatar;
@@ -231,7 +242,7 @@ public class Contact implements ListItem, Blockable {
for (final String tag : getSystemTags(true)) {
tags.add(new Tag(tag));
}
- Presence.Status status = getShownStatus();
+ final var status = getShownStatus();
if (!showInRoster() && getSystemAccount() != null) {
tags.add(new Tag("Android"));
}
@@ -308,6 +319,7 @@ public class Contact implements ListItem, Blockable {
public void updatePresence(final String resource, final Presence presence) {
this.presences.updatePresence(resource, presence);
+ refreshCaps();
}
public void removePresence(final String resource) {
@@ -321,7 +333,7 @@ public class Contact implements ListItem, Blockable {
refreshCaps();
}
- public Presence.Status getShownStatus() {
+ public im.conversations.android.xmpp.model.stanza.Presence.Availability getShownStatus() {
return this.presences.getShownStatus();
}
diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 7c432f545f8e240b8faf87eb80b9f2dd11d984a8..390fc10eeab4827ecbd7d378c356dfbeccaed744 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -21,11 +21,12 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
-import eu.siacs.conversations.xmpp.forms.Data;
-import eu.siacs.conversations.xmpp.forms.Field;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xml.Element;
+import im.conversations.android.xmpp.model.data.Data;
+import im.conversations.android.xmpp.model.data.Field;
+import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -52,7 +53,7 @@ public class MucOptions {
public OnRenameListener onRenameListener = null;
private boolean mAutoPushConfiguration = true;
private final Account account;
- private ServiceDiscoveryResult serviceDiscoveryResult;
+ private InfoQuery infoQuery;
private boolean isOnline = false;
private Error error = Error.NONE;
private User self;
@@ -121,15 +122,24 @@ public class MucOptions {
return MessageArchiveService.Version.has(getFeatures());
}
- public boolean updateConfiguration(ServiceDiscoveryResult serviceDiscoveryResult) {
- this.serviceDiscoveryResult = serviceDiscoveryResult;
+ private InfoQuery getServiceDiscoveryResult() {
+ return this.infoQuery;
+ }
+
+ public boolean updateConfiguration(final InfoQuery serviceDiscoveryResult) {
+ this.infoQuery = serviceDiscoveryResult;
+ final var roomInfo = getRoomInfoForm();
String name;
- Field roomConfigName = getRoomInfoForm().getFieldByName("muc#roomconfig_roomname");
+ Field roomConfigName =
+ roomInfo == null ? null : roomInfo.getFieldByName("muc#roomconfig_roomname");
if (roomConfigName != null) {
name = roomConfigName.getValue();
} else {
final var identities = serviceDiscoveryResult.getIdentities();
- final String identityName = !identities.isEmpty() ? identities.get(0).getName() : null;
+ final String identityName =
+ !identities.isEmpty()
+ ? Iterables.getFirst(identities, null).getIdentityName()
+ : null;
final Jid jid = conversation.getJid();
if (identityName != null && !identityName.equals(jid == null ? null : jid.getLocal())) {
name = identityName;
@@ -151,11 +161,11 @@ public class MucOptions {
}
private Data getRoomInfoForm() {
- final List forms =
- serviceDiscoveryResult == null
- ? Collections.emptyList()
- : serviceDiscoveryResult.forms;
- return forms.isEmpty() ? new Data() : forms.get(0);
+ final var serviceDiscoveryResult = getServiceDiscoveryResult();
+ return serviceDiscoveryResult == null
+ ? null
+ : serviceDiscoveryResult.getServiceDiscoveryExtension(
+ "http://jabber.org/protocol/muc#roominfo");
}
public String getAvatar() {
@@ -163,8 +173,9 @@ public class MucOptions {
}
public boolean hasFeature(String feature) {
- return this.serviceDiscoveryResult != null
- && this.serviceDiscoveryResult.features.contains(feature);
+ final var serviceDiscoveryResult = getServiceDiscoveryResult();
+ return serviceDiscoveryResult != null
+ && serviceDiscoveryResult.getFeatureStrings().contains(feature);
}
public boolean hasVCards() {
@@ -222,9 +233,10 @@ public class MucOptions {
return conversation.getBooleanAttribute(Conversation.ATTRIBUTE_MEMBERS_ONLY, false);
}
- public List getFeatures() {
- return this.serviceDiscoveryResult != null
- ? this.serviceDiscoveryResult.features
+ public Collection getFeatures() {
+ final var serviceDiscoveryResult = getServiceDiscoveryResult();
+ return serviceDiscoveryResult != null
+ ? serviceDiscoveryResult.getFeatureStrings()
: Collections.emptyList();
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java
deleted file mode 100644
index cabc2a4759b92826f465eef802bf740c847ba448..0000000000000000000000000000000000000000
--- a/src/main/java/eu/siacs/conversations/entities/Presence.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package eu.siacs.conversations.entities;
-
-import androidx.annotation.NonNull;
-
-import java.util.Locale;
-
-import eu.siacs.conversations.xml.Element;
-
-public class Presence implements Comparable {
-
- public enum Status {
- CHAT, ONLINE, AWAY, XA, DND, OFFLINE;
-
- public String toShowString() {
- switch(this) {
- case CHAT: return "chat";
- case AWAY: return "away";
- case XA: return "xa";
- case DND: return "dnd";
- }
- return null;
- }
-
- public static Status fromShowString(String show) {
- if (show == null) {
- return ONLINE;
- } else {
- switch (show.toLowerCase(Locale.US)) {
- case "away":
- return AWAY;
- case "xa":
- return XA;
- case "dnd":
- return DND;
- case "chat":
- return CHAT;
- default:
- return ONLINE;
- }
- }
- }
- }
-
- private final Status status;
- private ServiceDiscoveryResult disco;
- private final String ver;
- private final String hash;
- private final String node;
- private final String message;
-
- public Presence(Status status, String ver, String hash, String node, String message) {
- this.status = status;
- this.ver = ver;
- this.hash = hash;
- this.node = node;
- this.message = message;
- }
-
- public static Presence parse(String show, Element caps, String message) {
- final String hash = caps == null ? null : caps.getAttribute("hash");
- final String ver = caps == null ? null : caps.getAttribute("ver");
- final String node = caps == null ? null : caps.getAttribute("node");
- return new Presence(Status.fromShowString(show), ver, hash, node, message);
- }
-
- public int compareTo(@NonNull Presence other) {
- return this.status.compareTo(other.status);
- }
-
- public Status getStatus() {
- return this.status;
- }
-
- public boolean hasCaps() {
- return ver != null && hash != null;
- }
-
- public String getVer() {
- return this.ver;
- }
-
- public String getNode() {
- return this.node;
- }
-
- public String getHash() {
- return this.hash;
- }
-
- public String getMessage() {
- return this.message;
- }
-
- public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
- this.disco = disco;
- }
-
- public ServiceDiscoveryResult getServiceDiscoveryResult() {
- return disco;
- }
-}
diff --git a/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
index 958891d34d4b8915fdc3309a4827c37b41001347..0c36a42cf994bc84f0ff51559c2c0fb4450b7e95 100644
--- a/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
+++ b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues;
import android.database.Cursor;
+import im.conversations.android.xmpp.model.stanza.Presence;
import java.util.Objects;
public class PresenceTemplate extends AbstractEntity {
@@ -13,9 +14,9 @@ public class PresenceTemplate extends AbstractEntity {
private long lastUsed = 0;
private String statusMessage;
- private Presence.Status status = Presence.Status.ONLINE;
+ private Presence.Availability status = Presence.Availability.ONLINE;
- public PresenceTemplate(Presence.Status status, String statusMessage) {
+ public PresenceTemplate(Presence.Availability status, String statusMessage) {
this.status = status;
this.statusMessage = statusMessage;
this.lastUsed = System.currentTimeMillis();
@@ -41,11 +42,11 @@ public class PresenceTemplate extends AbstractEntity {
template.lastUsed = cursor.getLong(cursor.getColumnIndex(LAST_USED));
template.statusMessage = cursor.getString(cursor.getColumnIndex(MESSAGE));
template.status =
- Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS)));
+ Presence.Availability.valueOfShown(cursor.getString(cursor.getColumnIndex(STATUS)));
return template;
}
- public Presence.Status getStatus() {
+ public Presence.Availability getStatus() {
return status;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java
index eabc98f78b6963928470dc21661e18cb3f31fc19..c955b71d8b93be06aae55d6c84305995f96f8f1d 100644
--- a/src/main/java/eu/siacs/conversations/entities/Presences.java
+++ b/src/main/java/eu/siacs/conversations/entities/Presences.java
@@ -1,15 +1,26 @@
package eu.siacs.conversations.entities;
import android.util.Pair;
-
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import eu.siacs.conversations.xmpp.Jid;
+import eu.siacs.conversations.xmpp.manager.DiscoManager;
+import im.conversations.android.xmpp.model.disco.info.Identity;
+import im.conversations.android.xmpp.model.stanza.Presence;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Hashtable;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
public class Presences {
- private final Hashtable presences = new Hashtable<>();
+ private final HashMap presences = new HashMap<>();
+ private final Contact contact;
+
+ public Presences(final Contact contact) {
+ this.contact = contact;
+ }
private static String nameWithoutVersion(String name) {
String[] parts = name.split(" ");
@@ -63,18 +74,19 @@ public class Presences {
}
}
- public Presence.Status getShownStatus() {
- Presence.Status status = Presence.Status.OFFLINE;
+ public Presence.Availability getShownStatus() {
+ Presence.Availability highestAvailability = Presence.Availability.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();
+ for (final Presence p : presences.values()) {
+ final var availability = p.getAvailability();
+ if (availability == Presence.Availability.DND) {
+ return availability;
+ } else if (availability.compareTo(highestAvailability) < 0) {
+ highestAvailability = availability;
}
}
}
- return status;
+ return highestAvailability;
}
public int size() {
@@ -100,10 +112,12 @@ public class Presences {
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()));
+ for (Presence presence : this.presences.values()) {
+ String message = Strings.nullToEmpty(presence.getStatus()).trim();
+ if (Strings.isNullOrEmpty(message)) {
+ continue;
}
+ templates.add(new PresenceTemplate(presence.getAvailability(), message));
}
return templates;
}
@@ -115,24 +129,46 @@ public class Presences {
}
}
- public List getStatusMessages() {
- ArrayList messages = new ArrayList<>();
+ public Set getStatusMessages() {
+ Set messages = new HashSet<>();
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);
+ String message = Strings.nullToEmpty(presence.getStatus()).trim();
+ if (Strings.isNullOrEmpty(message)) {
+ continue;
}
+ messages.add(message);
}
}
return messages;
}
+ public Set getFullJids() {
+ final Set jids = new HashSet<>();
+ synchronized (this.presences) {
+ for (var resource : this.presences.keySet()) {
+ final var jid = Strings.isNullOrEmpty(resource) ? contact.getJid().asBareJid() : contact.getJid().withResource(resource);
+ jids.add(jid);
+ }
+ }
+ return jids;
+ }
+
public boolean allOrNonSupport(String namespace) {
+ final var connection = this.contact.getAccount().getXmppConnection();
+ if (connection == null) {
+ return true;
+ }
synchronized (this.presences) {
- for (Presence presence : this.presences.values()) {
- ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
- if (disco == null || !disco.getFeatures().contains(namespace)) {
+ for (var resource : this.presences.keySet()) {
+ final var disco =
+ connection
+ .getManager(DiscoManager.class)
+ .get(
+ Strings.isNullOrEmpty(resource)
+ ? contact.getJid().asBareJid()
+ : contact.getJid().withResource(resource));
+ if (disco == null || !disco.getFeatureStrings().contains(namespace)) {
return false;
}
}
@@ -141,45 +177,46 @@ public class Presences {
}
public boolean anySupport(final String namespace) {
- synchronized (this.presences) {
- if (this.presences.size() == 0) {
+ final var connection = this.contact.getAccount().getXmppConnection();
+ if (connection == null) {
+ return false;
+ }
+ final var jids = getFullJids();
+ if (jids.size() == 0) {
+ return true;
+ }
+ for (final var jid : jids) {
+ final var disco = connection.getManager(DiscoManager.class).get(jid);
+ if (disco != null && disco.getFeatures().contains(namespace)) {
return true;
}
- for (Presence presence : this.presences.values()) {
- ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
- if (disco != null && disco.getFeatures().contains(namespace)) {
- return true;
- }
- }
}
return false;
}
public String firstWhichSupport(final String namespace) {
- synchronized (this.presences) {
- for (Map.Entry entry : this.presences.entrySet()) {
- String resource = entry.getKey();
- Presence presence = entry.getValue();
- ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
- if (disco != null && disco.getFeatures().contains(namespace)) {
- return resource;
- }
+ final var connection = this.contact.getAccount().getXmppConnection();
+ if (connection == null) {
+ return null;
+ }
+ for (final var jid : getFullJids()) {
+ final var disco = connection.getManager(DiscoManager.class).get(jid);
+ if (disco != null && disco.getFeatures().contains(namespace)) {
+ return jid.getResource();
}
}
return null;
}
public boolean anyIdentity(final String category, final String type) {
- synchronized (this.presences) {
- if (this.presences.size() == 0) {
- // https://github.com/iNPUTmice/Conversations/issues/4230
- return false;
- }
- for (Presence presence : this.presences.values()) {
- ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
- if (disco != null && disco.hasIdentity(category, type)) {
- return true;
- }
+ final var connection = this.contact.getAccount().getXmppConnection();
+ if (connection == null) {
+ return false;
+ }
+ for (final var jid : getFullJids()) {
+ final var disco = connection.getManager(DiscoManager.class).get(jid);
+ if (disco != null && disco.hasIdentityWithCategoryAndType(category, type)) {
+ return true;
}
}
return false;
@@ -188,15 +225,25 @@ public class Presences {
public Pair