r/o support for vcard avatars. pep avatars will be prefered

This commit is contained in:
Daniel Gultsch 2015-05-05 06:17:34 +02:00
parent e6aa604ade
commit 5136bf9832
6 changed files with 127 additions and 28 deletions

View file

@ -15,6 +15,7 @@ import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class Contact implements ListItem, Blockable { public class Contact implements ListItem, Blockable {
public static final String TABLENAME = "contacts"; public static final String TABLENAME = "contacts";
@ -40,11 +41,11 @@ public class Contact implements ListItem, Blockable {
protected int subscription = 0; protected int subscription = 0;
protected String systemAccount; protected String systemAccount;
protected String photoUri; protected String photoUri;
protected String avatar;
protected JSONObject keys = new JSONObject(); protected JSONObject keys = new JSONObject();
protected JSONArray groups = new JSONArray(); protected JSONArray groups = new JSONArray();
protected Presences presences = new Presences(); protected Presences presences = new Presences();
protected Account account; protected Account account;
protected Avatar avatar;
public Contact(final String account, final String systemName, final String serverName, public Contact(final String account, final String systemName, final String serverName,
final Jid jid, final int subscription, final String photoUri, final Jid jid, final int subscription, final String photoUri,
@ -61,7 +62,11 @@ public class Contact implements ListItem, Blockable {
} catch (JSONException e) { } catch (JSONException e) {
this.keys = new JSONObject(); this.keys = new JSONObject();
} }
this.avatar = avatar; if (avatar != null) {
this.avatar = new Avatar();
this.avatar.sha1sum = avatar;
this.avatar.origin = Avatar.Origin.VCARD; //always assume worst
}
try { try {
this.groups = (groups == null ? new JSONArray() : new JSONArray(groups)); this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
} catch (JSONException e) { } catch (JSONException e) {
@ -187,7 +192,7 @@ public class Contact implements ListItem, Blockable {
values.put(SYSTEMACCOUNT, systemAccount); values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri); values.put(PHOTOURI, photoUri);
values.put(KEYS, keys.toString()); values.put(KEYS, keys.toString());
values.put(AVATAR, avatar); values.put(AVATAR, avatar == null ? null : avatar.getFilename());
values.put(LAST_PRESENCE, lastseen.presence); values.put(LAST_PRESENCE, lastseen.presence);
values.put(LAST_TIME, lastseen.time); values.put(LAST_TIME, lastseen.time);
values.put(GROUPS, groups.toString()); values.put(GROUPS, groups.toString());
@ -411,17 +416,20 @@ public class Contact implements ListItem, Blockable {
return getJid().toDomainJid(); return getJid().toDomainJid();
} }
public boolean setAvatar(String filename) { public boolean setAvatar(Avatar avatar) {
if (this.avatar != null && this.avatar.equals(filename)) { if (this.avatar != null && this.avatar.equals(avatar)) {
return false; return false;
} else { } else {
this.avatar = filename; if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) {
return false;
}
this.avatar = avatar;
return true; return true;
} }
} }
public String getAvatar() { public String getAvatar() {
return this.avatar; return avatar == null ? null : avatar.getFilename();
} }
public boolean deleteOtrFingerprint(String fingerprint) { public boolean deleteOtrFingerprint(String fingerprint) {
@ -478,6 +486,10 @@ public class Contact implements ListItem, Blockable {
} }
} }
public boolean isSelf() {
return account.getJid().toBareJid().equals(getJid().toBareJid());
}
public static class Lastseen { public static class Lastseen {
public long time; public long time;
public String presence; public String presence;

View file

@ -91,7 +91,7 @@ public class IqGenerator extends AbstractGenerator {
return publish("urn:xmpp:avatar:metadata", item); return publish("urn:xmpp:avatar:metadata", item);
} }
public IqPacket retrieveAvatar(final Avatar avatar) { public IqPacket retrievePepAvatar(final Avatar avatar) {
final Element item = new Element("item"); final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum); item.setAttribute("id", avatar.sha1sum);
final IqPacket packet = retrieve("urn:xmpp:avatar:data", item); final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
@ -99,6 +99,13 @@ public class IqGenerator extends AbstractGenerator {
return packet; return packet;
} }
public IqPacket retrieveVcardAvatar(final Avatar avatar) {
final IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
packet.setTo(avatar.owner);
packet.addChild("vCard","vcard-temp");
return packet;
}
public IqPacket retrieveAvatarMetaData(final Jid to) { public IqPacket retrieveAvatarMetaData(final Jid to) {
final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
if (to != null) { if (to != null) {

View file

@ -494,7 +494,7 @@ public class MessageParser extends AbstractParser implements
} else { } else {
Contact contact = account.getRoster().getContact( Contact contact = account.getRoster().getContact(
from); from);
contact.setAvatar(avatar.getFilename()); contact.setAvatar(avatar);
mXmppConnectionService.getAvatarService().clear( mXmppConnectionService.getAvatarService().clear(
contact); contact);
mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateConversationUi();

View file

@ -13,6 +13,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements public class PresenceParser extends AbstractParser implements
@ -101,6 +102,20 @@ public class PresenceParser extends AbstractParser implements
if (nick != null) { if (nick != null) {
contact.setPresenceName(nick.getContent()); contact.setPresenceName(nick.getContent());
} }
Element x = packet.findChild("x","vcard-temp:x:update");
Avatar avatar = Avatar.parsePresence(x);
if (avatar != null && !contact.isSelf()) {
avatar.owner = from.toBareJid();
if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
if (contact.setAvatar(avatar)) {
mXmppConnectionService.getAvatarService().clear(contact);
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateRosterUi();
}
} else {
mXmppConnectionService.fetchAvatar(account,avatar);
}
}
mXmppConnectionService.updateRosterUi(); mXmppConnectionService.updateRosterUi();
} }

View file

@ -1893,9 +1893,19 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
fetchAvatar(account, avatar, null); fetchAvatar(account, avatar, null);
} }
public void fetchAvatar(Account account, final Avatar avatar, public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
final UiCallback<Avatar> callback) { switch (avatar.origin) {
IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar); case PEP:
fetchAvatarPep(account,avatar,callback);
break;
case VCARD:
fetchAvatarVcard(account, avatar, callback);
break;
}
}
private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar);
sendIqPacket(account, packet, new OnIqPacketReceived() { sendIqPacket(account, packet, new OnIqPacketReceived() {
@Override @Override
@ -1916,7 +1926,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else { } else {
Contact contact = account.getRoster() Contact contact = account.getRoster()
.getContact(avatar.owner); .getContact(avatar.owner);
contact.setAvatar(avatar.getFilename()); contact.setAvatar(avatar);
getAvatarService().clear(contact); getAvatarService().clear(contact);
updateConversationUi(); updateConversationUi();
updateRosterUi(); updateRosterUi();
@ -1925,8 +1935,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
callback.success(avatar); callback.success(avatar);
} }
Log.d(Config.LOGTAG, account.getJid().toBareJid() Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": succesfully fetched avatar for " + ": succesfuly fetched pep avatar for " + avatar.owner);
+ avatar.owner);
return; return;
} }
} else { } else {
@ -1949,8 +1958,34 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}); });
} }
public void checkForAvatar(Account account, private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
final UiCallback<Avatar> callback) { IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar);
this.sendIqPacket(account,packet,new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
Element vCard = packet.findChild("vCard","vcard-temp");
Element photo = vCard != null ? vCard.findChild("PHOTO") : null;
Element binval = photo != null ? photo.findChild("BINVAL") : null;
if (binval != null) {
avatar.image = binval.getContent();
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();
}
}
}
}
});
}
public void checkForAvatar(Account account, final UiCallback<Avatar> callback) {
IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null);
this.sendIqPacket(account, packet, new OnIqPacketReceived() { this.sendIqPacket(account, packet, new OnIqPacketReceived() {
@ -1972,7 +2007,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
getAvatarService().clear(account); getAvatarService().clear(account);
callback.success(avatar); callback.success(avatar);
} else { } else {
fetchAvatar(account, avatar, callback); fetchAvatarPep(account, avatar, callback);
} }
return; return;
} }

View file

@ -6,6 +6,9 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import android.util.Base64; import android.util.Base64;
public class Avatar { public class Avatar {
public enum Origin { PEP, VCARD };
public String type; public String type;
public String sha1sum; public String sha1sum;
public String image; public String image;
@ -13,21 +16,14 @@ public class Avatar {
public int width; public int width;
public long size; public long size;
public Jid owner; public Jid owner;
public Origin origin = Origin.PEP; //default to maintain compat
public byte[] getImageAsBytes() { public byte[] getImageAsBytes() {
return Base64.decode(image, Base64.DEFAULT); return Base64.decode(image, Base64.DEFAULT);
} }
public String getFilename() { public String getFilename() {
if (type == null) {
return sha1sum; return sha1sum;
} else if (type.equalsIgnoreCase("image/webp")) {
return sha1sum + ".webp";
} else if (type.equalsIgnoreCase("image/png")) {
return sha1sum + ".png";
} else {
return sha1sum;
}
} }
public static Avatar parseMetadata(Element items) { public static Avatar parseMetadata(Element items) {
@ -64,10 +60,44 @@ public class Avatar {
return null; return null;
} }
avatar.type = child.getAttribute("type"); avatar.type = child.getAttribute("type");
avatar.sha1sum = child.getAttribute("id"); String hash = child.getAttribute("id");
if (!isValidSHA1(hash)) {
return null;
}
avatar.sha1sum = hash;
avatar.origin = Origin.PEP;
return avatar; return avatar;
} }
} }
return null; return null;
} }
@Override
public boolean equals(Object object) {
if (object != null && object instanceof Avatar) {
Avatar other = (Avatar) object;
return other.getFilename().equals(this.getFilename());
} else {
return false;
}
}
public static Avatar parsePresence(Element x) {
Element photo = x != null ? x.findChild("photo") : null;
String hash = photo != null ? photo.getContent() : null;
if (hash == null) {
return null;
}
if (!isValidSHA1(hash)) {
return null;
}
Avatar avatar = new Avatar();
avatar.sha1sum = hash;
avatar.origin = Origin.VCARD;
return avatar;
}
private static boolean isValidSHA1(String s) {
return s != null && s.matches("[a-fA-F0-9]{40}");
}
} }