diff options
Diffstat (limited to 'src/main/java/eu/siacs/conversations/entities')
11 files changed, 479 insertions, 147 deletions
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 4c4a1916..b3a31127 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -46,6 +46,8 @@ public class Account extends AbstractEntity { public static final String DISPLAY_NAME = "display_name"; public static final String HOSTNAME = "hostname"; public static final String PORT = "port"; + public static final String STATUS = "status"; + public static final String STATUS_MESSAGE = "status_message"; public static final String PINNED_MECHANISM_KEY = "pinned_mechanism"; @@ -53,6 +55,7 @@ public class Account extends AbstractEntity { public static final int OPTION_DISABLED = 1; public static final int OPTION_REGISTER = 2; public static final int OPTION_USECOMPRESSION = 3; + public static final int OPTION_MAGIC_CREATE = 4; public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>(); public boolean httpUploadAvailable(long filesize) { @@ -79,6 +82,10 @@ public class Account extends AbstractEntity { } } + public Contact getSelfContact() { + return getRoster().getContact(jid); + } + public enum State { DISABLED, OFFLINE, @@ -93,7 +100,9 @@ public class Account extends AbstractEntity { REGISTRATION_NOT_SUPPORTED(true), SECURITY_ERROR(true), INCOMPATIBLE_SERVER(true), - TOR_NOT_AVAILABLE(true); + TOR_NOT_AVAILABLE(true), + BIND_FAILURE(true), + HOST_UNKNOWN(true); private final boolean isError; @@ -139,6 +148,10 @@ public class Account extends AbstractEntity { return R.string.account_status_incompatible_server; case TOR_NOT_AVAILABLE: return R.string.account_status_tor_unavailable; + case BIND_FAILURE: + return R.string.account_status_bind_failure; + case HOST_UNKNOWN: + return R.string.account_status_host_unknown; default: return R.string.account_status_unknown; } @@ -171,6 +184,8 @@ public class Account extends AbstractEntity { private final Roster roster = new Roster(this); private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>(); + private Presence.Status presenceStatus = Presence.Status.ONLINE; + private String presenceStatusMessage = null; public Account() { this.uuid = "0"; @@ -178,12 +193,13 @@ public class Account extends AbstractEntity { public Account(final Jid jid, final String password) { this(java.util.UUID.randomUUID().toString(), jid, - password, 0, null, "", null, null, null, 5222); + password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null); } private Account(final String uuid, final Jid jid, final String password, final int options, final String rosterVersion, final String keys, - final String avatar, String displayName, String hostname, int port) { + final String avatar, String displayName, String hostname, int port, + final Presence.Status status, String statusMessage) { this.uuid = uuid; this.jid = jid; if (jid.isBareJid()) { @@ -201,6 +217,8 @@ public class Account extends AbstractEntity { this.displayName = displayName; this.hostname = hostname; this.port = port; + this.presenceStatus = status; + this.presenceStatusMessage = statusMessage; } public static Account fromCursor(final Cursor cursor) { @@ -219,7 +237,9 @@ public class Account extends AbstractEntity { cursor.getString(cursor.getColumnIndex(AVATAR)), cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)), cursor.getString(cursor.getColumnIndex(HOSTNAME)), - cursor.getInt(cursor.getColumnIndex(PORT))); + cursor.getInt(cursor.getColumnIndex(PORT)), + Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))), + cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE))); } public boolean isOptionSet(final int option) { @@ -290,6 +310,22 @@ public class Account extends AbstractEntity { return getXmppConnection() != null && getStatus().isError() && getXmppConnection().getAttempt() >= 3; } + public void setPresenceStatus(Presence.Status status) { + this.presenceStatus = status; + } + + public Presence.Status getPresenceStatus() { + return this.presenceStatus; + } + + public void setPresenceStatusMessage(String message) { + this.presenceStatusMessage = message; + } + + public String getPresenceStatusMessage() { + return this.presenceStatusMessage; + } + public String getResource() { return jid.getResourcepart(); } @@ -350,6 +386,8 @@ public class Account extends AbstractEntity { values.put(DISPLAY_NAME, displayName); values.put(HOSTNAME, hostname); values.put(PORT, port); + values.put(STATUS, presenceStatus.toShowString()); + values.put(STATUS_MESSAGE, presenceStatusMessage); return values; } @@ -420,7 +458,7 @@ public class Account extends AbstractEntity { } public int countPresences() { - return this.getRoster().getContact(this.getJid().toBareJid()).getPresences().size(); + return this.getSelfContact().getPresences().size(); } public String getPgpSignature() { @@ -458,10 +496,10 @@ public class Account extends AbstractEntity { try { return keys.getLong(KEY_PGP_ID); } catch (JSONException e) { - return -1; + return 0; } } else { - return -1; + return 0; } } diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java index fa30443d..428758d6 100644 --- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java +++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.entities; import android.graphics.Color; +import android.content.Context; import java.util.ArrayList; import java.util.List; @@ -63,9 +64,7 @@ public class Bookmark extends Element implements ListItem { @Override public String getDisplayJid() { Jid jid = getJid(); - if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && jid != null && jid.getDomainpart().equals(Config.CONFERENCE_DOMAIN_LOCK)) { - return jid.getLocalpart(); - } else if (jid != null) { + if (jid != null) { return jid.toString(); } else { return null; @@ -78,7 +77,7 @@ public class Bookmark extends Element implements ListItem { } @Override - public List<Tag> getTags() { + public List<Tag> getTags(Context context) { ArrayList<Tag> tags = new ArrayList<Tag>(); for (Element element : getChildren()) { if (element.getName().equals("group") && element.getContent() != null) { @@ -121,7 +120,8 @@ public class Bookmark extends Element implements ListItem { } } - public boolean match(String needle) { + @Override + public boolean match(Context context, String needle) { if (needle == null) { return true; } @@ -129,12 +129,12 @@ public class Bookmark extends Element implements ListItem { final Jid jid = getJid(); return (jid != null && jid.toString().contains(needle)) || getDisplayName().toLowerCase(Locale.US).contains(needle) || - matchInTag(needle); + matchInTag(context, needle); } - private boolean matchInTag(String needle) { + private boolean matchInTag(Context context, String needle) { needle = needle.toLowerCase(Locale.US); - for (Tag tag : getTags()) { + for (Tag tag : getTags(context)) { if (tag.getName().toLowerCase(Locale.US).contains(needle)) { return true; } diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 9b4be366..60e5d5fc 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -124,9 +124,7 @@ public class Contact implements ListItem, Blockable { @Override public String getDisplayJid() { - if (Config.LOCK_DOMAINS_IN_CONVERSATIONS && jid != null && jid.getDomainpart().equals(Config.DOMAIN_LOCK)) { - return jid.getLocalpart(); - } else if (jid != null) { + if (jid != null) { return jid.toString(); } else { return null; @@ -142,25 +140,14 @@ public class Contact implements ListItem, Blockable { } @Override - public List<Tag> getTags() { + public List<Tag> getTags(Context context) { final ArrayList<Tag> tags = new ArrayList<>(); for (final String group : getGroups()) { tags.add(new Tag(group, UIHelper.getColorForName(group))); } - switch (getMostAvailableStatus()) { - case CHAT: - case ONLINE: - tags.add(new Tag("online", 0xff259b24)); - break; - case AWAY: - tags.add(new Tag("away", 0xffff9800)); - break; - case XA: - tags.add(new Tag("not available", 0xfff44336)); - break; - case DND: - tags.add(new Tag("dnd", 0xfff44336)); - break; + Presence.Status status = getMostAvailableStatus(); + if (status != Presence.Status.OFFLINE) { + tags.add(UIHelper.getTagForStatus(context, status)); } if (isBlocked()) { tags.add(new Tag("blocked", 0xff2e2f3b)); @@ -173,7 +160,7 @@ public class Contact implements ListItem, Blockable { return UIHelper.getStatusColor(getMostAvailableStatus()); } - public boolean match(String needle) { + public boolean match(Context context, String needle) { if (needle == null || needle.isEmpty()) { return true; } @@ -181,7 +168,7 @@ public class Contact implements ListItem, Blockable { String[] parts = needle.split("\\s+"); if (parts.length > 1) { for(int i = 0; i < parts.length; ++i) { - if (!match(parts[i])) { + if (!match(context, parts[i])) { return false; } } @@ -189,13 +176,13 @@ public class Contact implements ListItem, Blockable { } else { return jid.toString().contains(needle) || getDisplayName().toLowerCase(Locale.US).contains(needle) || - matchInTag(needle); + matchInTag(context, needle); } } - private boolean matchInTag(String needle) { + private boolean matchInTag(Context context, String needle) { needle = needle.toLowerCase(Locale.US); - for (Tag tag : getTags()) { + for (Tag tag : getTags(context)) { if (tag.getName().toLowerCase(Locale.US).contains(needle)) { return true; } diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 7878cecd..fe03daac 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -29,6 +29,7 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; + public class Conversation extends AbstractEntity implements Blockable { public static final String TABLENAME = "conversations"; @@ -619,23 +620,6 @@ public class Conversation extends AbstractEntity implements Blockable { return this.nextCounterpart; } - private int getMostRecentlyUsedOutgoingEncryption() { - synchronized (this.messages) { - for(int i = this.messages.size() -1; i >= 0; --i) { - final Message m = this.messages.get(i); - if (!m.isCarbon() && m.getStatus() != Message.STATUS_RECEIVED) { - final int e = m.getEncryption(); - if (e == Message.ENCRYPTION_DECRYPTED || e == Message.ENCRYPTION_DECRYPTION_FAILED) { - return Message.ENCRYPTION_PGP; - } else { - return e; - } - } - } - } - return Message.ENCRYPTION_NONE; - } - private int getMostRecentlyUsedIncomingEncryption() { synchronized (this.messages) { for(int i = this.messages.size() -1; i >= 0; --i) { @@ -657,24 +641,21 @@ public class Conversation extends AbstractEntity implements Blockable { final AxolotlService axolotlService = getAccount().getAxolotlService(); int next = this.getIntAttribute(ATTRIBUTE_NEXT_ENCRYPTION, -1); if (next == -1) { - if (Config.X509_VERIFICATION) { - if (axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) { + if (Config.supportOmemo() + && axolotlService != null + && mode == MODE_SINGLE + && axolotlService.isConversationAxolotlCapable(this) + && getAccount().getSelfContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY) + && getContact().getPresences().allOrNonSupport(AxolotlService.PEP_DEVICE_LIST_NOTIFY)) { return Message.ENCRYPTION_AXOLOTL; } else { - return Message.ENCRYPTION_NONE; - } - } - int outgoing = this.getMostRecentlyUsedOutgoingEncryption(); - if (outgoing == Message.ENCRYPTION_NONE) { next = this.getMostRecentlyUsedIncomingEncryption(); - } else { - next = outgoing; } } if (!Config.supportUnencrypted() && next <= 0) { if (Config.supportOmemo() - && (axolotlService != null && axolotlService.isConversationAxolotlCapable(this) || !Config.multipleEncryptionChoices())) { + && ((axolotlService != null && axolotlService.isConversationAxolotlCapable(this)) || !Config.multipleEncryptionChoices())) { return Message.ENCRYPTION_AXOLOTL; } else if (Config.supportOtr() && mode == MODE_SINGLE) { return Message.ENCRYPTION_OTR; diff --git a/src/main/java/eu/siacs/conversations/entities/ListItem.java b/src/main/java/eu/siacs/conversations/entities/ListItem.java index 56804fbf..640cb267 100644 --- a/src/main/java/eu/siacs/conversations/entities/ListItem.java +++ b/src/main/java/eu/siacs/conversations/entities/ListItem.java @@ -1,5 +1,7 @@ package eu.siacs.conversations.entities; +import android.content.Context; + import java.util.List; import eu.siacs.conversations.xmpp.jid.Jid; @@ -13,7 +15,7 @@ public interface ListItem extends Comparable<ListItem> { public int getStatusColor(); - List<Tag> getTags(); + List<Tag> getTags(Context context); final class Tag { private final String name; @@ -33,5 +35,5 @@ public interface ListItem extends Comparable<ListItem> { } } - boolean match(final String needle); + boolean match(Context context, final String needle); } diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 6faebc65..95ee879d 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -570,13 +570,8 @@ public class Message extends AbstractEntity { boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}"); if (encrypted) { - if (MimeUtils.guessMimeTypeFromExtension(extension) != null) { mTreatAsDownloadAble = Decision.MUST; return mTreatAsDownloadAble; - } else { - mTreatAsDownloadAble = Decision.NEVER; - return mTreatAsDownloadAble; - } } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension) || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) { mTreatAsDownloadAble = Decision.SHOULD; diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 7681a3d4..44d16cf2 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -5,9 +5,7 @@ import android.annotation.SuppressLint; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Set; import eu.siacs.conversations.R; @@ -28,6 +26,19 @@ public class MucOptions { this.self = user; } + public void changeAffiliation(Jid jid, Affiliation affiliation) { + User user = findUserByRealJid(jid); + synchronized (users) { + if (user != null && user.getRole() == Role.NONE) { + users.remove(user); + if (affiliation.ranks(Affiliation.MEMBER)) { + user.affiliation = affiliation; + users.add(user); + } + } + } + } + public enum Affiliation { OWNER("owner", 4, R.string.owner), ADMIN("admin", 3, R.string.admin), @@ -124,10 +135,10 @@ public class MucOptions { } - public static class User { + public static class User implements Comparable<User> { private Role role = Role.NONE; private Affiliation affiliation = Affiliation.NONE; - private Jid jid; + private Jid realJid; private Jid fullJid; private long pgpKeyId = 0; private Avatar avatar; @@ -139,15 +150,11 @@ public class MucOptions { } public String getName() { - return this.fullJid.getResourcepart(); + return fullJid == null ? null : fullJid.getResourcepart(); } - public void setJid(Jid jid) { - this.jid = jid; - } - - public Jid getJid() { - return this.jid; + public void setRealJid(Jid jid) { + this.realJid = jid != null ? jid.toBareJid() : null; } public Role getRole() { @@ -155,6 +162,10 @@ public class MucOptions { } public void setRole(String role) { + if (role == null) { + this.role = Role.NONE; + return; + } role = role.toLowerCase(); switch (role) { case "moderator": @@ -172,26 +183,15 @@ public class MucOptions { } } - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (!(other instanceof User)) { - return false; - } else { - User o = (User) other; - return getName() != null && getName().equals(o.getName()) - && jid != null && jid.equals(o.jid) - && affiliation == o.affiliation - && role == o.role; - } - } - public Affiliation getAffiliation() { return this.affiliation; } public void setAffiliation(String affiliation) { + if (affiliation == null) { + this.affiliation = Affiliation.NONE; + return; + } affiliation = affiliation.toLowerCase(); switch (affiliation) { case "admin": @@ -220,7 +220,13 @@ public class MucOptions { } public Contact getContact() { - return getAccount().getRoster().getContactFromRoster(getJid()); + if (fullJid != null) { + return getAccount().getRoster().getContactFromRoster(realJid); + } else if (realJid != null){ + return getAccount().getRoster().getContact(realJid); + } else { + return null; + } } public boolean setAvatar(Avatar avatar) { @@ -243,11 +249,62 @@ public class MucOptions { public Jid getFullJid() { return fullJid; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + User user = (User) o; + + if (role != user.role) return false; + if (affiliation != user.affiliation) return false; + if (realJid != null ? !realJid.equals(user.realJid) : user.realJid != null) + return false; + return fullJid != null ? fullJid.equals(user.fullJid) : user.fullJid == null; + + } + + @Override + public int hashCode() { + int result = role != null ? role.hashCode() : 0; + result = 31 * result + (affiliation != null ? affiliation.hashCode() : 0); + result = 31 * result + (realJid != null ? realJid.hashCode() : 0); + result = 31 * result + (fullJid != null ? fullJid.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "[fulljid:"+String.valueOf(fullJid)+",realjid:"+String.valueOf(realJid)+",affiliation"+affiliation.toString()+"]"; + } + + public boolean realJidMatchesAccount() { + return realJid != null && realJid.equals(options.account.getJid().toBareJid()); + } + + @Override + public int compareTo(User another) { + Contact ourContact = getContact(); + Contact anotherContact = another.getContact(); + if (ourContact != null && anotherContact != null) { + return ourContact.compareTo(anotherContact); + } else if (ourContact == null && anotherContact != null) { + return getName().compareToIgnoreCase(anotherContact.getDisplayName()); + } else if (ourContact != null) { + return ourContact.getDisplayName().compareToIgnoreCase(another.getName()); + } else { + return getName().compareToIgnoreCase(another.getName()); + } + } + + public Jid getRealJid() { + return realJid; + } } private Account account; - private final Map<String, User> users = Collections.synchronizedMap(new LinkedHashMap<String, User>()); - private final Set<Jid> members = Collections.synchronizedSet(new HashSet<Jid>()); + private final Set<User> users = new HashSet<>(); private final List<String> features = new ArrayList<>(); private Data form = new Data(); private Conversation conversation; @@ -315,20 +372,82 @@ public class MucOptions { return hasFeature("muc_moderated"); } - public User deleteUser(String name) { - return this.users.remove(name); + public User deleteUser(Jid jid) { + User user = findUserByFullJid(jid); + if (user != null) { + synchronized (users) { + users.remove(user); + if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) { + user.role = Role.NONE; + user.avatar = null; + user.fullJid = null; + users.add(user); + } + } + } + return user; } public void addUser(User user) { - this.users.put(user.getName(), user); + User old; + if (user.fullJid == null && user.realJid != null) { + old = findUserByRealJid(user.realJid); + if (old != null) { + if (old.fullJid != null) { + return; //don't add. user already exists + } else { + synchronized (users) { + users.remove(old); + } + } + } + } else if (user.realJid != null) { + old = findUserByRealJid(user.realJid); + synchronized (users) { + if (old != null && old.fullJid == null) { + users.remove(old); + } + } + } + old = findUserByFullJid(user.getFullJid()); + synchronized (this.users) { + if (old != null) { + users.remove(old); + } + this.users.add(user); + } } - public User findUser(String name) { - return this.users.get(name); + public User findUserByFullJid(Jid jid) { + if (jid == null) { + return null; + } + synchronized (users) { + for (User user : users) { + if (jid.equals(user.getFullJid())) { + return user; + } + } + } + return null; } - public boolean isUserInRoom(String name) { - return findUser(name) != null; + public User findUserByRealJid(Jid jid) { + if (jid == null) { + return null; + } + synchronized (users) { + for (User user : users) { + if (jid.equals(user.realJid)) { + return user; + } + } + } + return null; + } + + public boolean isUserInRoom(Jid jid) { + return findUserByFullJid(jid) != null; } public void setError(Error error) { @@ -341,7 +460,23 @@ public class MucOptions { } public ArrayList<User> getUsers() { - return new ArrayList<>(users.values()); + return getUsers(true); + } + + public ArrayList<User> getUsers(boolean includeOffline) { + synchronized (users) { + if (includeOffline) { + return new ArrayList<>(users); + } else { + ArrayList<User> onlineUsers = new ArrayList<>(); + for (User user : users) { + if (user.getRole().ranks(Role.PARTICIPANT)) { + onlineUsers.add(user); + } + } + return onlineUsers; + } + } } public List<User> getUsers(int max) { @@ -350,7 +485,9 @@ public class MucOptions { } public int getUserCount() { - return this.users.size(); + synchronized (users) { + return users.size(); + } } public String getProposedNick() { @@ -386,7 +523,9 @@ public class MucOptions { } public void setOffline() { + synchronized (users) { this.users.clear(); + } this.error = Error.NO_RESPONSE; this.isOnline = false; } @@ -404,13 +543,13 @@ public class MucOptions { } public String createNameFromParticipants() { - if (users.size() >= 2) { + if (getUserCount() >= 2) { List<String> names = new ArrayList<>(); for (User user : getUsers(5)) { Contact contact = user.getContact(); if (contact != null && !contact.getDisplayName().isEmpty()) { names.add(contact.getDisplayName().split("\\s+")[0]); - } else { + } else if (user.getName() != null){ names.add(user.getName()); } } @@ -429,7 +568,7 @@ public class MucOptions { public long[] getPgpKeyIds() { List<Long> ids = new ArrayList<>(); - for (User user : this.users.values()) { + for (User user : this.users) { if (user.getPgpKeyId() != 0) { ids.add(user.getPgpKeyId()); } @@ -443,20 +582,24 @@ public class MucOptions { } public boolean pgpKeysInUse() { - for (User user : this.users.values()) { + synchronized (users) { + for (User user : users) { if (user.getPgpKeyId() != 0) { return true; } } + } return false; } public boolean everybodyHasKeys() { - for (User user : this.users.values()) { + synchronized (users) { + for (User user : users) { if (user.getPgpKeyId() == 0) { return false; } } + } return true; } @@ -468,12 +611,12 @@ public class MucOptions { } } - public Jid getTrueCounterpart(String name) { - if (name.equals(getSelf().getName())) { + public Jid getTrueCounterpart(Jid jid) { + if (jid.equals(getSelf().getFullJid())) { return account.getJid().toBareJid(); } - User user = findUser(name); - return user == null ? null : user.getJid(); + User user = findUserByFullJid(jid); + return user == null ? null : user.realJid; } public String getPassword() { @@ -499,11 +642,15 @@ public class MucOptions { return this.conversation; } - public void putMember(Jid jid) { - members.add(jid); - } - public List<Jid> getMembers() { - return new ArrayList<>(members); + ArrayList<Jid> members = new ArrayList<>(); + synchronized (users) { + for (User user : users) { + if (user.affiliation.ranks(Affiliation.MEMBER) && user.realJid != null) { + members.add(user.realJid); + } + } + } + return members; } } diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java index 442f1bca..e9f6d0d1 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presence.java +++ b/src/main/java/eu/siacs/conversations/entities/Presence.java @@ -17,42 +17,47 @@ public class Presence implements Comparable { case XA: return "xa"; case DND: return "dnd"; } - return null; } - } - protected final Status status; - protected ServiceDiscoveryResult disco; - protected final String ver; - protected final String hash; - - private Presence(Status status, String ver, String hash) { - this.status = status; - this.ver = ver; - this.hash = hash; - } - - public static Presence parse(String show, Element caps) { - final String hash = caps == null ? null : caps.getAttribute("hash"); - final String ver = caps == null ? null : caps.getAttribute("ver"); + public static Status fromShowString(String show) { if (show == null) { - return new Presence(Status.ONLINE, ver, hash); + return ONLINE; } else { switch (show.toLowerCase(Locale.US)) { case "away": - return new Presence(Status.AWAY, ver, hash); + return AWAY; case "xa": - return new Presence(Status.XA, ver, hash); + return XA; case "dnd": - return new Presence(Status.DND, ver, hash); + return DND; case "chat": - return new Presence(Status.CHAT, ver, hash); + return CHAT; default: - return new Presence(Status.ONLINE, ver, hash); + return ONLINE; } } } + } + + private final Status status; + private ServiceDiscoveryResult disco; + private final String ver; + private final String hash; + private final String message; + + private Presence(Status status, String ver, String hash, String message) { + this.status = status; + this.ver = ver; + this.hash = hash; + 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"); + return new Presence(Status.fromShowString(show), ver, hash, message); + } public int compareTo(Object other) { return this.status.compareTo(((Presence)other).status); @@ -74,7 +79,15 @@ public class Presence implements Comparable { 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 new file mode 100644 index 00000000..c268b24c --- /dev/null +++ b/src/main/java/eu/siacs/conversations/entities/PresenceTemplate.java @@ -0,0 +1,76 @@ +package eu.siacs.conversations.entities; + +import android.content.ContentValues; +import android.database.Cursor; + + +public class PresenceTemplate extends AbstractEntity { + + public static final String TABELNAME = "presence_templates"; + public static final String LAST_USED = "last_used"; + public static final String MESSAGE = "message"; + public static final String STATUS = "status"; + + private long lastUsed = 0; + private String statusMessage; + private Presence.Status status = Presence.Status.ONLINE; + + public PresenceTemplate(Presence.Status status, String statusMessage) { + this.status = status; + this.statusMessage = statusMessage; + this.lastUsed = System.currentTimeMillis(); + this.uuid = java.util.UUID.randomUUID().toString(); + } + + private PresenceTemplate() { + + } + + @Override + public ContentValues getContentValues() { + final String show = status.toShowString(); + ContentValues values = new ContentValues(); + values.put(LAST_USED, lastUsed); + values.put(MESSAGE, statusMessage); + values.put(STATUS, show == null ? "" : show); + values.put(UUID, uuid); + return values; + } + + public static PresenceTemplate fromCursor(Cursor cursor) { + PresenceTemplate template = new PresenceTemplate(); + template.uuid = cursor.getString(cursor.getColumnIndex(UUID)); + 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))); + return template; + } + + public Presence.Status getStatus() { + return status; + } + + public String getStatusMessage() { + return statusMessage; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PresenceTemplate template = (PresenceTemplate) o; + + if (statusMessage != null ? !statusMessage.equals(template.statusMessage) : template.statusMessage != null) + return false; + return status == template.status; + + } + + @Override + public int hashCode() { + int result = statusMessage != null ? statusMessage.hashCode() : 0; + result = 31 * result + status.hashCode(); + return result; + } +} diff --git a/src/main/java/eu/siacs/conversations/entities/Presences.java b/src/main/java/eu/siacs/conversations/entities/Presences.java index 813eda7a..c8078331 100644 --- a/src/main/java/eu/siacs/conversations/entities/Presences.java +++ b/src/main/java/eu/siacs/conversations/entities/Presences.java @@ -1,8 +1,11 @@ package eu.siacs.conversations.entities; +import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.Iterator; +import java.util.List; +import java.util.Map; import eu.siacs.conversations.xml.Element; @@ -52,9 +55,44 @@ public class Presences { } } + public List<PresenceTemplate> asTemplates() { + synchronized (this.presences) { + ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size()); + for(Presence p : presences.values()) { + templates.add(new PresenceTemplate(p.getStatus(),p.getMessage())); + } + return templates; + } + } + public boolean has(String presence) { synchronized (this.presences) { return presences.containsKey(presence); } } + + public List<String> getStatusMessages() { + ArrayList<String> 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 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; + } } diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index a8f60e39..40499ede 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -53,6 +53,7 @@ public class ServiceDiscoveryResult { } public Identity(final JSONObject o) { + this( o.optString("category", null), o.optString("type", null), @@ -157,6 +158,54 @@ public class ServiceDiscoveryResult { this.features.add(features.getString(i)); } } + JSONArray forms = o.optJSONArray("forms"); + if (forms != null) { + for(int i = 0; i < forms.length(); i++) { + this.forms.add(createFormFromJSONObject(forms.getJSONObject(i))); + } + } + } + + private static Data createFormFromJSONObject(JSONObject o) { + Data data = new Data(); + JSONArray names = o.names(); + for(int i = 0; i < names.length(); ++i) { + try { + String name = names.getString(i); + JSONArray jsonValues = o.getJSONArray(name); + ArrayList<String> values = new ArrayList<>(jsonValues.length()); + for(int j = 0; j < jsonValues.length(); ++j) { + values.add(jsonValues.getString(j)); + } + data.put(name, values); + } catch (Exception e) { + e.printStackTrace(); + } + } + return data; + } + + private static JSONObject createJSONFromForm(Data data) { + JSONObject object = new JSONObject(); + for(Field field : data.getFields()) { + try { + JSONArray jsonValues = new JSONArray(); + for(String value : field.getValues()) { + jsonValues.put(value); + } + object.put(field.getFieldName(), jsonValues); + } catch(Exception e) { + e.printStackTrace(); + } + } + try { + JSONArray jsonValues = new JSONArray(); + jsonValues.put(data.getFormType()); + object.put(Data.FORM_TYPE, jsonValues); + } catch(Exception e) { + e.printStackTrace(); + } + return object; } public String getVer() { @@ -273,10 +322,16 @@ public class ServiceDiscoveryResult { for(Identity id : this.getIdentities()) { ids.put(id.toJSON()); } - o.put("identites", ids); + o.put("identities", ids); o.put("features", new JSONArray(this.getFeatures())); + JSONArray forms = new JSONArray(); + for(Data data : this.forms) { + forms.put(createJSONFromForm(data)); + } + o.put("forms", forms); + return o; } catch(JSONException e) { return null; |