From ba1e38796eae7150377e4d07d5ebfdb63a8a5999 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 3 Dec 2015 00:46:28 +0100 Subject: parse vcard avatars from muc presences --- .../eu/siacs/conversations/entities/Message.java | 13 ++ .../siacs/conversations/entities/MucOptions.java | 163 +++++++-------------- .../siacs/conversations/parser/MessageParser.java | 2 +- .../siacs/conversations/parser/PresenceParser.java | 110 +++++++++++++- .../conversations/services/AvatarService.java | 39 ++++- .../services/XmppConnectionService.java | 22 ++- .../ui/ConferenceDetailsActivity.java | 5 +- 7 files changed, 229 insertions(+), 125 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index b59b0b38..49f1e330 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -240,6 +240,19 @@ public class Message extends AbstractEntity { } } + public Object getUserOrContact() { + if (this.conversation.getMode() == Conversation.MODE_SINGLE) { + return this.conversation.getContact(); + } else { + if (this.trueCounterpart == null) { + return getConversation().getMucOptions().findUser(getCounterpart().getResourcepart()); + } else { + return this.conversation.getAccount().getRoster() + .getContactFromRoster(this.trueCounterpart); + } + } + } + public String getBody() { return body; } diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 13a5bb9f..07fdeac4 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -7,17 +7,23 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.PgpEngine; -import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.forms.Data; import eu.siacs.conversations.xmpp.forms.Field; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; -import eu.siacs.conversations.xmpp.stanzas.PresencePacket; +import eu.siacs.conversations.xmpp.pep.Avatar; @SuppressLint("DefaultLocale") public class MucOptions { + public Account getAccount() { + return this.conversation.getAccount(); + } + + public void setSelf(User user) { + this.self = user; + } + public enum Affiliation { OWNER("owner", 4, R.string.owner), ADMIN("admin", 3, R.string.admin), @@ -100,21 +106,29 @@ public class MucOptions { public static final String STATUS_CODE_LOST_MEMBERSHIP = "321"; private interface OnEventListener { - public void onSuccess(); + void onSuccess(); - public void onFailure(); + void onFailure(); } public interface OnRenameListener extends OnEventListener { } - public class User { + public static class User { private Role role = Role.NONE; private Affiliation affiliation = Affiliation.NONE; private String name; private Jid jid; + private Jid fullJid; private long pgpKeyId = 0; + private Avatar avatar; + private MucOptions options; + + public User(MucOptions options, Jid from) { + this.options = options; + this.fullJid = from; + } public String getName() { return name; @@ -202,7 +216,23 @@ public class MucOptions { } public Contact getContact() { - return account.getRoster().getContactFromRoster(getJid()); + return getAccount().getRoster().getContactFromRoster(getJid()); + } + + public void setAvatar(Avatar avatar) { + this.avatar = avatar; + } + + public String getAvatar() { + return avatar == null ? null : avatar.getFilename(); + } + + public Account getAccount() { + return options.getAccount(); + } + + public Jid getFullJid() { + return fullJid; } } @@ -213,15 +243,16 @@ public class MucOptions { private Conversation conversation; private boolean isOnline = false; private int error = ERROR_UNKNOWN; - private OnRenameListener onRenameListener = null; - private User self = new User(); + public OnRenameListener onRenameListener = null; + private User self; private String subject = null; private String password = null; - private boolean mNickChangingInProgress = false; + public boolean mNickChangingInProgress = false; public MucOptions(Conversation conversation) { this.account = conversation.getAccount(); this.conversation = conversation; + this.self = new User(this,conversation.getJid()); } public void updateFeatures(ArrayList features) { @@ -291,113 +322,25 @@ public class MucOptions { users.add(user); } - public boolean isUserInRoom(String name) { - for (int i = 0; i < users.size(); ++i) { - if (users.get(i).getName().equals(name)) { - return true; - } + public User findUser(String name) { + if (name == null) { + return null; } - return false; - } - - public void processPacket(PresencePacket packet, PgpEngine pgp) { - final Jid from = packet.getFrom(); - if (!from.isBareJid()) { - final String name = from.getResourcepart(); - final String type = packet.getAttribute("type"); - final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user"); - final List codes = getStatusCodes(x); - if (type == null) { - User user = new User(); - if (x != null) { - Element item = x.findChild("item"); - if (item != null && name != null) { - user.setName(name); - user.setAffiliation(item.getAttribute("affiliation")); - user.setRole(item.getAttribute("role")); - user.setJid(item.getAttributeAsJid("jid")); - if (codes.contains(STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(this.conversation.getJid())) { - this.isOnline = true; - this.error = ERROR_NO_ERROR; - self = user; - if (mNickChangingInProgress) { - if (onRenameListener != null) { - onRenameListener.onSuccess(); - } - mNickChangingInProgress = false; - } - } else { - addUser(user); - } - if (pgp != null) { - Element signed = packet.findChild("x", "jabber:x:signed"); - if (signed != null) { - Element status = packet.findChild("status"); - String msg = status == null ? "" : status.getContent(); - long keyId = pgp.fetchKeyId(account, msg, signed.getContent()); - if (keyId != 0) { - user.setPgpKeyId(keyId); - } - } - } - } - } - } else if (type.equals("unavailable")) { - if (codes.contains(STATUS_CODE_SELF_PRESENCE) || - packet.getFrom().equals(this.conversation.getJid())) { - if (codes.contains(STATUS_CODE_CHANGED_NICK)) { - this.mNickChangingInProgress = true; - } else if (codes.contains(STATUS_CODE_KICKED)) { - setError(KICKED_FROM_ROOM); - } else if (codes.contains(STATUS_CODE_BANNED)) { - setError(ERROR_BANNED); - } else if (codes.contains(STATUS_CODE_LOST_MEMBERSHIP)) { - setError(ERROR_MEMBERS_ONLY); - } else { - setError(ERROR_UNKNOWN); - } - } else { - deleteUser(name); - } - } else if (type.equals("error")) { - Element error = packet.findChild("error"); - if (error != null && error.hasChild("conflict")) { - if (isOnline) { - if (onRenameListener != null) { - onRenameListener.onFailure(); - } - } else { - setError(ERROR_NICK_IN_USE); - } - } else if (error != null && error.hasChild("not-authorized")) { - setError(ERROR_PASSWORD_REQUIRED); - } else if (error != null && error.hasChild("forbidden")) { - setError(ERROR_BANNED); - } else if (error != null && error.hasChild("registration-required")) { - setError(ERROR_MEMBERS_ONLY); - } + for (User user : users) { + if (user.getName().equals(name)) { + return user; } } + return null; } - private void setError(int error) { - this.isOnline = false; - this.error = error; + public boolean isUserInRoom(String name) { + return findUser(name) != null; } - private List getStatusCodes(Element x) { - List codes = new ArrayList<>(); - if (x != null) { - for (Element child : x.getChildren()) { - if (child.getName().equals("status")) { - String code = child.getAttribute("code"); - if (code != null) { - codes.add(code); - } - } - } - } - return codes; + public void setError(int error) { + this.isOnline = error == ERROR_NO_ERROR; + this.error = error; } public List getUsers() { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index b4c648fe..3e923af1 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -169,7 +169,7 @@ public class MessageParser extends AbstractParser implements if ("urn:xmpp:avatar:metadata".equals(node)) { Avatar avatar = Avatar.parseMetadata(items); if (avatar != null) { - avatar.owner = from; + avatar.owner = from.toBareJid(); if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { if (account.getJid().toBareJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 59b94bc0..bf49d5c7 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -1,7 +1,11 @@ package eu.siacs.conversations.parser; +import android.util.Log; + import java.util.ArrayList; +import java.util.List; +import eu.siacs.conversations.Config; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -32,7 +36,7 @@ public class PresenceParser extends AbstractParser implements boolean before = mucOptions.online(); int count = mucOptions.getUsers().size(); final ArrayList tileUserBefore = new ArrayList<>(mucOptions.getUsers().subList(0,Math.min(mucOptions.getUsers().size(),5))); - mucOptions.processPacket(packet, mPgpEngine); + processConferencePresence(packet, mucOptions); final ArrayList tileUserAfter = new ArrayList<>(mucOptions.getUsers().subList(0,Math.min(mucOptions.getUsers().size(),5))); if (!tileUserAfter.equals(tileUserBefore)) { mXmppConnectionService.getAvatarService().clear(conversation); @@ -45,6 +49,110 @@ public class PresenceParser extends AbstractParser implements } } + private void processConferencePresence(PresencePacket packet, MucOptions mucOptions) { + final Jid from = packet.getFrom(); + if (!from.isBareJid()) { + final String name = from.getResourcepart(); + final String type = packet.getAttribute("type"); + final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user"); + Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); + final List codes = getStatusCodes(x); + if (type == null) { + MucOptions.User user = new MucOptions.User(mucOptions,from); + if (x != null) { + Element item = x.findChild("item"); + if (item != null && name != null) { + user.setName(name); + user.setAffiliation(item.getAttribute("affiliation")); + user.setRole(item.getAttribute("role")); + user.setJid(item.getAttributeAsJid("jid")); + if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(mucOptions.getConversation().getJid())) { + mucOptions.setError(MucOptions.ERROR_NO_ERROR); + mucOptions.setSelf(user); + if (mucOptions.mNickChangingInProgress) { + if (mucOptions.onRenameListener != null) { + mucOptions.onRenameListener.onSuccess(); + } + mucOptions.mNickChangingInProgress = false; + } + } else { + mucOptions.addUser(user); + } + if (mXmppConnectionService.getPgpEngine() != null) { + Element signed = packet.findChild("x", "jabber:x:signed"); + if (signed != null) { + Element status = packet.findChild("status"); + String msg = status == null ? "" : status.getContent(); + long keyId = mXmppConnectionService.getPgpEngine().fetchKeyId(mucOptions.getAccount(), msg, signed.getContent()); + if (keyId != 0) { + user.setPgpKeyId(keyId); + } + } + } + if (avatar != null) { + avatar.owner = from; + if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + user.setAvatar(avatar); + } else { + mXmppConnectionService.fetchAvatar(mucOptions.getAccount(), avatar); + } + Log.d(Config.LOGTAG, "user " + avatar.owner + " has avatar"); + } + } + } + } else if (type.equals("unavailable")) { + if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || + packet.getFrom().equals(mucOptions.getConversation().getJid())) { + if (codes.contains(MucOptions.STATUS_CODE_CHANGED_NICK)) { + mucOptions.mNickChangingInProgress = true; + } else if (codes.contains(MucOptions.STATUS_CODE_KICKED)) { + mucOptions.setError(MucOptions.KICKED_FROM_ROOM); + } else if (codes.contains(MucOptions.STATUS_CODE_BANNED)) { + mucOptions.setError(MucOptions.ERROR_BANNED); + } else if (codes.contains(MucOptions.STATUS_CODE_LOST_MEMBERSHIP)) { + mucOptions.setError(MucOptions.ERROR_MEMBERS_ONLY); + } else { + mucOptions.setError(MucOptions.ERROR_UNKNOWN); + } + } else { + mucOptions.deleteUser(name); + } + } else if (type.equals("error")) { + Element error = packet.findChild("error"); + if (error != null && error.hasChild("conflict")) { + if (mucOptions.online()) { + if (mucOptions.onRenameListener != null) { + mucOptions.onRenameListener.onFailure(); + } + } else { + mucOptions.setError(MucOptions.ERROR_NICK_IN_USE); + } + } else if (error != null && error.hasChild("not-authorized")) { + mucOptions.setError(MucOptions.ERROR_PASSWORD_REQUIRED); + } else if (error != null && error.hasChild("forbidden")) { + mucOptions.setError(MucOptions.ERROR_BANNED); + } else if (error != null && error.hasChild("registration-required")) { + mucOptions.setError(MucOptions.ERROR_MEMBERS_ONLY); + } + } + } + } + + private static List getStatusCodes(Element x) { + List codes = new ArrayList<>(); + if (x != null) { + for (Element child : x.getChildren()) { + if (child.getName().equals("status")) { + String code = child.getAttribute("code"); + if (code != null) { + codes.add(code); + } + } + } + } + return codes; + } + public void parseContactPresence(final PresencePacket packet, final Account account) { final PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator(); final Jid from = packet.getFrom(); diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java index 5db4abae..68ab6053 100644 --- a/src/main/java/eu/siacs/conversations/services/AvatarService.java +++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java @@ -58,6 +58,22 @@ public class AvatarService { return avatar; } + public Bitmap get(final MucOptions.User user, final int size, boolean cachedOnly) { + final String KEY = key(user, size); + Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY); + if (avatar != null || cachedOnly) { + return avatar; + } + if (user.getAvatar() != null) { + avatar = mXmppConnectionService.getFileBackend().getAvatar(user.getAvatar(), size); + } + if (avatar == null) { + avatar = get(user.getName(), size, cachedOnly); + } + this.mXmppConnectionService.getBitmapCache().put(KEY, avatar); + return avatar; + } + public void clear(Contact contact) { synchronized (this.sizes) { for (Integer size : sizes) { @@ -77,6 +93,16 @@ public class AvatarService { + contact.getJid() + "_" + String.valueOf(size); } + private String key(MucOptions.User user, int size) { + synchronized (this.sizes) { + if (!this.sizes.contains(size)) { + this.sizes.add(size); + } + } + return PREFIX_CONTACT + "_" + user.getAccount().getJid().toBareJid() + "_" + + user.getFullJid() + "_" + String.valueOf(size); + } + public Bitmap get(ListItem item, int size) { return get(item,size,false); } @@ -199,9 +225,11 @@ public class AvatarService { public Bitmap get(Message message, int size, boolean cachedOnly) { if (message.getStatus() == Message.STATUS_RECEIVED) { - Contact contact = message.getContact(); - if (contact != null) { - return get(contact, size, cachedOnly); + Object object = message.getUserOrContact(); + if (object != null && object instanceof Contact) { + return get((Contact) object, size, cachedOnly); + } else if (object != null && object instanceof MucOptions.User) { + return get((MucOptions.User) object, size, cachedOnly); } else { return get(UIHelper.getMessageDisplayName(message), size, cachedOnly); } @@ -290,6 +318,11 @@ public class AvatarService { if (drawTile(canvas, uri, left, top, right, bottom)) { return true; } + } else if (user.getAvatar() != null) { + Uri uri = mXmppConnectionService.getFileBackend().getAvatarUri(user.getAvatar()); + if (drawTile(canvas, uri, left, top, right, bottom)) { + return true; + } } String name = contact != null ? contact.getDisplayName() : user.getName(); drawTile(canvas, name, left, top, right, bottom); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 8e4c9c68..2bafb03a 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -2358,12 +2358,22 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (getFileBackend().save(avatar)) { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": successfully fetched vCard avatar for " + avatar.owner); - Contact contact = account.getRoster() - .getContact(avatar.owner); - contact.setAvatar(avatar); - getAvatarService().clear(contact); - updateConversationUi(); - updateRosterUi(); + if (avatar.owner.isBareJid()) { + Contact contact = account.getRoster() + .getContact(avatar.owner); + contact.setAvatar(avatar); + getAvatarService().clear(contact); + updateConversationUi(); + updateRosterUi(); + } else { + Conversation conversation = find(account,avatar.owner.toBareJid()); + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + MucOptions.User user = conversation.getMucOptions().findUser(avatar.owner.getResourcepart()); + if (user != null) { + user.setAvatar(avatar); + } + } + } } } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index a2abbf52..d1e76c1d 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -527,20 +527,17 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers }); tvKey.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId())); } - Bitmap bm; Contact contact = user.getContact(); if (contact != null) { - bm = avatarService().get(contact, getPixel(48)); tvDisplayName.setText(contact.getDisplayName()); tvStatus.setText(user.getName() + " \u2022 " + getStatus(user)); } else { - bm = avatarService().get(user.getName(), getPixel(48)); tvDisplayName.setText(user.getName()); tvStatus.setText(getStatus(user)); } ImageView iv = (ImageView) view.findViewById(R.id.contact_photo); - iv.setImageBitmap(bm); + iv.setImageBitmap(avatarService().get(user, getPixel(48), false)); membersView.addView(view); if (mConversation.getMucOptions().canInvite()) { mInviteButton.setVisibility(View.VISIBLE); -- cgit v1.2.3