r/o support for vcard avatars. pep avatars will be prefered
This commit is contained in:
parent
e6aa604ade
commit
5136bf9832
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue