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.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
public class Contact implements ListItem, Blockable {
public static final String TABLENAME = "contacts";
@ -40,11 +41,11 @@ public class Contact implements ListItem, Blockable {
protected int subscription = 0;
protected String systemAccount;
protected String photoUri;
protected String avatar;
protected JSONObject keys = new JSONObject();
protected JSONArray groups = new JSONArray();
protected Presences presences = new Presences();
protected Account account;
protected Avatar avatar;
public Contact(final String account, final String systemName, final String serverName,
final Jid jid, final int subscription, final String photoUri,
@ -61,7 +62,11 @@ public class Contact implements ListItem, Blockable {
} catch (JSONException e) {
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 {
this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
} catch (JSONException e) {
@ -187,7 +192,7 @@ public class Contact implements ListItem, Blockable {
values.put(SYSTEMACCOUNT, systemAccount);
values.put(PHOTOURI, photoUri);
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_TIME, lastseen.time);
values.put(GROUPS, groups.toString());
@ -411,17 +416,20 @@ public class Contact implements ListItem, Blockable {
return getJid().toDomainJid();
}
public boolean setAvatar(String filename) {
if (this.avatar != null && this.avatar.equals(filename)) {
public boolean setAvatar(Avatar avatar) {
if (this.avatar != null && this.avatar.equals(avatar)) {
return false;
} 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;
}
}
public String getAvatar() {
return this.avatar;
return avatar == null ? null : avatar.getFilename();
}
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 long time;
public String presence;

View file

@ -91,7 +91,7 @@ public class IqGenerator extends AbstractGenerator {
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");
item.setAttribute("id", avatar.sha1sum);
final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
@ -99,6 +99,13 @@ public class IqGenerator extends AbstractGenerator {
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) {
final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null);
if (to != null) {

View file

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

View file

@ -13,6 +13,7 @@ import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
@ -101,6 +102,20 @@ public class PresenceParser extends AbstractParser implements
if (nick != null) {
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();
}

View file

@ -1893,9 +1893,19 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
fetchAvatar(account, avatar, null);
}
public void fetchAvatar(Account account, final Avatar avatar,
final UiCallback<Avatar> callback) {
IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar);
public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) {
switch (avatar.origin) {
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() {
@Override
@ -1916,7 +1926,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else {
Contact contact = account.getRoster()
.getContact(avatar.owner);
contact.setAvatar(avatar.getFilename());
contact.setAvatar(avatar);
getAvatarService().clear(contact);
updateConversationUi();
updateRosterUi();
@ -1925,8 +1935,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
callback.success(avatar);
}
Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": succesfully fetched avatar for "
+ avatar.owner);
+ ": succesfuly fetched pep avatar for " + avatar.owner);
return;
}
} else {
@ -1949,8 +1958,34 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
});
}
public void checkForAvatar(Account account,
final UiCallback<Avatar> callback) {
private void fetchAvatarVcard(final Account account, final Avatar avatar, 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);
this.sendIqPacket(account, packet, new OnIqPacketReceived() {
@ -1972,7 +2007,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
getAvatarService().clear(account);
callback.success(avatar);
} else {
fetchAvatar(account, avatar, callback);
fetchAvatarPep(account, avatar, callback);
}
return;
}

View file

@ -6,6 +6,9 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import android.util.Base64;
public class Avatar {
public enum Origin { PEP, VCARD };
public String type;
public String sha1sum;
public String image;
@ -13,21 +16,14 @@ public class Avatar {
public int width;
public long size;
public Jid owner;
public Origin origin = Origin.PEP; //default to maintain compat
public byte[] getImageAsBytes() {
return Base64.decode(image, Base64.DEFAULT);
}
public String getFilename() {
if (type == null) {
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) {
@ -64,10 +60,44 @@ public class Avatar {
return null;
}
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 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}");
}
}