diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 5ca575459..7f169c5d5 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; @@ -27,6 +28,7 @@ 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.jingle.RtpCapability; import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.Jid; @@ -46,6 +48,7 @@ public class Contact implements ListItem, Blockable { public static final String LAST_PRESENCE = "last_presence"; public static final String LAST_TIME = "last_time"; public static final String GROUPS = "groups"; + public static final String RTP_CAPABILITY = "rtpCapability"; private String accountUuid; private String systemName; private String serverName; @@ -64,11 +67,12 @@ public class Contact implements ListItem, Blockable { private boolean mActive = false; private long mLastseen = 0; private String mLastPresence = null; + private RtpCapability.Capability rtpCapability; public Contact(final String account, final String systemName, final String serverName, final String presenceName, final Jid jid, final int subscription, final String photoUri, final Uri systemAccount, final String keys, final String avatar, final long lastseen, - final String presence, final String groups) { + final String presence, final String groups, final RtpCapability.Capability rtpCapability) { this.accountUuid = account; this.systemName = systemName; this.serverName = serverName; @@ -96,6 +100,7 @@ public class Contact implements ListItem, Blockable { } this.mLastseen = lastseen; this.mLastPresence = presence; + this.rtpCapability = rtpCapability; } public Contact(final Jid jid) { @@ -129,7 +134,8 @@ public class Contact implements ListItem, Blockable { cursor.getString(cursor.getColumnIndex(AVATAR)), cursor.getLong(cursor.getColumnIndex(LAST_TIME)), cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)), - cursor.getString(cursor.getColumnIndex(GROUPS))); + cursor.getString(cursor.getColumnIndex(GROUPS)), + RtpCapability.Capability.of(cursor.getString(cursor.getColumnIndex(RTP_CAPABILITY)))); } public String getDisplayName() { @@ -234,6 +240,7 @@ public class Contact implements ListItem, Blockable { values.put(LAST_PRESENCE, mLastPresence); values.put(LAST_TIME, mLastseen); values.put(GROUPS, groups.toString()); + values.put(RTP_CAPABILITY, rtpCapability == null ? null : rtpCapability.toString()); return values; } } @@ -573,7 +580,18 @@ public class Contact implements ListItem, Blockable { return (avatar != null && avatar.getFilename() != null) || presenceName != null; } - public final class Options { + public boolean refreshRtpCapability() { + final RtpCapability.Capability previous = this.rtpCapability; + this.rtpCapability = RtpCapability.check(this, false); + return !Objects.equals(previous, this.rtpCapability); + } + + public RtpCapability.Capability getRtpCapability() { + + return this.rtpCapability; + } + + public static final class Options { public static final int TO = 0; public static final int FROM = 1; public static final int ASKING = 2; diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java index 741ec03e0..9dce29d39 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presence.java +++ b/src/main/java/eu/siacs/conversations/entities/Presence.java @@ -7,7 +7,7 @@ import java.util.Locale; import eu.siacs.conversations.xml.Element; -public class Presence implements Comparable { +public class Presence implements Comparable { public enum Status { CHAT, ONLINE, AWAY, XA, DND, OFFLINE; @@ -64,7 +64,7 @@ public class Presence implements Comparable { return new Presence(Status.fromShowString(show), ver, hash, node, message); } - public int compareTo(@NonNull Object other) { + public int compareTo(@NonNull Presence other) { return this.status.compareTo(((Presence)other).status); } diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java index feca98839..04d378cc2 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presences.java +++ b/src/main/java/eu/siacs/conversations/entities/Presences.java @@ -136,6 +136,9 @@ public class Presences { public boolean anySupport(final String namespace) { synchronized (this.presences) { + if (this.presences.size() == 0) { + return true; + } for (Presence presence : this.presences.values()) { ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult(); if (disco != null && disco.getFeatures().contains(namespace)) { diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 789b07c42..4d11cfa1a 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -63,9 +63,9 @@ import eu.siacs.conversations.xmpp.Jid; public class DatabaseBackend extends SQLiteOpenHelper { private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 47; + private static final int DATABASE_VERSION = 48; private static DatabaseBackend instance = null; - private static String CREATE_CONTATCS_STATEMENT = "create table " + private static final String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT," + Contact.PRESENCE_NAME + " TEXT," @@ -73,12 +73,13 @@ public class DatabaseBackend extends SQLiteOpenHelper { + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER," + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, " + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, " + + Contact.RTP_CAPABILITY + " TEXT," + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", " + Contact.JID + ") ON CONFLICT REPLACE);"; - private static String CREATE_DISCOVERY_RESULTS_STATEMENT = "create table " + private static final String CREATE_DISCOVERY_RESULTS_STATEMENT = "create table " + ServiceDiscoveryResult.TABLENAME + "(" + ServiceDiscoveryResult.HASH + " TEXT, " + ServiceDiscoveryResult.VER + " TEXT, " @@ -86,7 +87,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + "UNIQUE(" + ServiceDiscoveryResult.HASH + ", " + ServiceDiscoveryResult.VER + ") ON CONFLICT REPLACE);"; - private static String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE " + private static final String CREATE_PRESENCE_TEMPLATES_STATEMENT = "CREATE TABLE " + PresenceTemplate.TABELNAME + "(" + PresenceTemplate.UUID + " TEXT, " + PresenceTemplate.LAST_USED + " NUMBER," @@ -94,7 +95,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + PresenceTemplate.STATUS + " TEXT," + "UNIQUE(" + PresenceTemplate.MESSAGE + "," + PresenceTemplate.STATUS + ") ON CONFLICT REPLACE);"; - private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE " + private static final String CREATE_PREKEYS_STATEMENT = "CREATE TABLE " + SQLiteAxolotlStore.PREKEY_TABLENAME + "(" + SQLiteAxolotlStore.ACCOUNT + " TEXT, " + SQLiteAxolotlStore.ID + " INTEGER, " @@ -559,6 +560,9 @@ public class DatabaseBackend extends SQLiteOpenHelper { if (oldVersion < 47 && newVersion >= 47) { db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.PRESENCE_NAME + " TEXT"); } + if (oldVersion < 48 && newVersion >= 48) { + db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN " + Contact.RTP_CAPABILITY + " TEXT"); + } } private void canonicalizeJids(SQLiteDatabase db) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 6cce735dd..23e156f67 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -358,10 +358,10 @@ public class XmppConnectionService extends Service { syncDirtyContacts(account); } }; - private AtomicLong mLastExpiryRun = new AtomicLong(0); + private final AtomicLong mLastExpiryRun = new AtomicLong(0); private SecureRandom mRandom; - private LruCache, ServiceDiscoveryResult> discoCache = new LruCache<>(20); - private OnStatusChanged statusListener = new OnStatusChanged() { + private final LruCache, ServiceDiscoveryResult> discoCache = new LruCache<>(20); + private final OnStatusChanged statusListener = new OnStatusChanged() { @Override public void onStatusChanged(final Account account) { @@ -4552,6 +4552,10 @@ public class XmppConnectionService extends Service { final ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key); if (disco != null) { presence.setServiceDiscoveryResult(disco); + final Contact contact = account.getRoster().getContact(jid); + if (contact.refreshRtpCapability()) { + syncRoster(account); + } } else { if (account.inProgressDiscoFetches.contains(key)) { Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping duplicate disco request for " + key.second + " to " + jid); @@ -4585,12 +4589,21 @@ public class XmppConnectionService extends Service { } private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) { + boolean rosterNeedsSync = false; for (final Contact contact : roster.getContacts()) { + boolean serviceDiscoverySet = false; for (final Presence presence : contact.getPresences().getPresences()) { if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) { presence.setServiceDiscoveryResult(disco); + serviceDiscoverySet = true; } } + if (serviceDiscoverySet) { + rosterNeedsSync |= contact.refreshRtpCapability(); + } + } + if (rosterNeedsSync) { + syncRoster(roster.getAccount()); } } 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 7fad0459a..fb7a60480 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpCapability.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.xmpp.jingle; +import com.google.common.base.Strings; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -20,7 +22,7 @@ public class RtpCapability { Namespace.JINGLE_APPS_RTP, Namespace.JINGLE_APPS_DTLS ); - private static List VIDEO_REQUIREMENTS = Arrays.asList( + private static final List VIDEO_REQUIREMENTS = Arrays.asList( Namespace.JINGLE_FEATURE_AUDIO, Namespace.JINGLE_FEATURE_VIDEO ); @@ -42,7 +44,7 @@ public class RtpCapability { 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()) { + for (final Map.Entry presence : presences.getPresencesMap().entrySet()) { final Capability capability = check(presence.getValue()); if (capability == Capability.NONE) { continue; @@ -55,9 +57,16 @@ public class RtpCapability { } public static Capability check(final Contact contact) { + return check(contact, true); + } + + public static Capability check(final Contact contact, final boolean allowFallback) { final Presences presences = contact.getPresences(); + if (presences.size() == 0 && allowFallback) { + return contact.getRtpCapability(); + } Capability result = Capability.NONE; - for(Presence presence : presences.getPresences()) { + for (final Presence presence : presences.getPresences()) { Capability capability = check(presence); if (capability == Capability.VIDEO) { result = capability; @@ -69,7 +78,18 @@ public class RtpCapability { } public enum Capability { - NONE, AUDIO, VIDEO + NONE, AUDIO, VIDEO; + + public static Capability of(String value) { + if (Strings.isNullOrEmpty(value)) { + return NONE; + } + try { + return valueOf(value); + } catch (IllegalArgumentException e) { + return NONE; + } + } } }