Detailed changes
@@ -87,13 +87,6 @@ public class IqGenerator extends AbstractGenerator {
return packet;
}
- public Iq retrieveVcardAvatar(final Jid to) {
- final Iq packet = new Iq(Iq.Type.GET);
- packet.setTo(to);
- packet.addChild("vCard", "vcard-temp");
- return packet;
- }
-
public Iq retrieveAvatarMetaData(final Jid to) {
final Iq packet = retrieve("urn:xmpp:avatar:metadata", null);
if (to != null) {
@@ -491,7 +491,6 @@ public class PresenceParser extends AbstractParser
@Override
public void accept(final im.conversations.android.xmpp.model.stanza.Presence packet) {
- // Log.d(Config.LOGTAG,"<--"+packet);
if (packet.hasChild("x", Namespace.MUC_USER)) {
this.parseConferencePresence(packet);
} else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) {
@@ -1220,7 +1220,7 @@ public class FileBackend {
try {
ByteArrayOutputStream mByteArrayOutputStream = new ByteArrayOutputStream();
Base64OutputStream mBase64OutputStream =
- new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT);
+ new Base64OutputStream(mByteArrayOutputStream, Base64.DEFAULT | Base64.NO_WRAP);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
DigestOutputStream mDigestOutputStream =
new DigestOutputStream(mBase64OutputStream, digest);
@@ -4366,53 +4366,39 @@ public class XmppConnectionService extends Service {
}
private void publishMucAvatar(
- Conversation conversation, Avatar avatar, OnAvatarPublication callback) {
+ final Conversation conversation,
+ final Avatar avatar,
+ final OnAvatarPublication callback) {
final var account = conversation.getAccount();
- final Iq retrieve = mIqGenerator.retrieveVcardAvatar(avatar);
- sendIqPacket(
- account,
- retrieve,
- (response) -> {
- boolean itemNotFound =
- response.getType() == Iq.Type.ERROR
- && response.hasChild("error")
- && response.findChild("error").hasChild("item-not-found");
- if (response.getType() == Iq.Type.RESULT || itemNotFound) {
- Element vcard = response.findChild("vCard", "vcard-temp");
- if (vcard == null) {
- vcard = new Element("vCard", "vcard-temp");
- }
- Element photo = vcard.findChild("PHOTO");
- if (photo == null) {
- photo = vcard.addChild("PHOTO");
- }
- photo.clearChildren();
- photo.addChild("TYPE").setContent(avatar.type);
- photo.addChild("BINVAL").setContent(avatar.image);
- final Iq publication = new Iq(Iq.Type.SET);
- publication.setTo(conversation.getJid().asBareJid());
- publication.addChild(vcard);
- sendIqPacket(
- account,
- publication,
- (publicationResponse) -> {
- if (publicationResponse.getType() == Iq.Type.RESULT) {
- callback.onAvatarPublicationSucceeded();
- } else {
- Log.d(
- Config.LOGTAG,
- "failed to publish vcard "
- + publicationResponse.getErrorCondition());
- callback.onAvatarPublicationFailed(
- R.string.error_publish_avatar_server_reject);
- }
- });
- } else {
- Log.d(Config.LOGTAG, "failed to request vcard " + response);
+ final var connection = account.getXmppConnection();
+ final var future =
+ connection
+ .getManager(VCardManager.class)
+ .publishPhoto(
+ avatar.owner,
+ avatar.type,
+ BaseEncoding.base64().decode(avatar.image));
+ Futures.addCallback(
+ future,
+ new FutureCallback<>() {
+ @Override
+ public void onSuccess(Void result) {
+ Log.d(Config.LOGTAG, "published muc avatar");
+ final var c = account.getRoster().getContact(avatar.owner);
+ c.setAvatar(avatar);
+ getAvatarService().clear(c);
+ getAvatarService().clear(conversation.getMucOptions());
+ callback.onAvatarPublicationSucceeded();
+ }
+
+ @Override
+ public void onFailure(@NonNull Throwable t) {
+ Log.d(Config.LOGTAG, "could not publish muc avatar", t);
callback.onAvatarPublicationFailed(
- R.string.error_publish_avatar_no_server_support);
+ R.string.error_publish_avatar_server_reject);
}
- });
+ },
+ MoreExecutors.directExecutor());
}
public void cancelAvatarFetches(final Account account) {
@@ -4563,6 +4549,8 @@ public class XmppConnectionService extends Service {
getAvatarService().clear(account);
updateAccountUi();
} else {
+ // TODO if this is a MUC clear MucOptions too
+ // TODO do the same clearing for when setting a cached version
final Contact contact = account.getRoster().getContact(avatar.owner);
contact.setAvatar(avatar);
account.getXmppConnection().getManager(RosterManager.class).writeToDatabaseAsync();
@@ -8,7 +8,11 @@ import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
+import im.conversations.android.xmpp.IqErrorException;
+import im.conversations.android.xmpp.model.error.Condition;
import im.conversations.android.xmpp.model.stanza.Iq;
+import im.conversations.android.xmpp.model.vcard.BinaryValue;
+import im.conversations.android.xmpp.model.vcard.Photo;
import im.conversations.android.xmpp.model.vcard.VCard;
import java.util.Objects;
@@ -54,8 +58,12 @@ public class VCardManager extends AbstractManager {
}
public ListenableFuture<Void> publish(final VCard vCard) {
+ return publish(getAccount().getJid().asBareJid(), vCard);
+ }
+
+ public ListenableFuture<Void> publish(final Jid address, final VCard vCard) {
final var iq = new Iq(Iq.Type.SET, vCard);
- iq.setTo(getAccount().getJid().asBareJid());
+ iq.setTo(address);
return Futures.transform(
connection.sendIqPacket(iq), result -> null, MoreExecutors.directExecutor());
}
@@ -78,4 +86,42 @@ public class VCardManager extends AbstractManager {
},
MoreExecutors.directExecutor());
}
+
+ public ListenableFuture<Void> publishPhoto(
+ final Jid address, final String type, final byte[] image) {
+ final var retrieveFuture = this.retrieve(address);
+
+ final var caughtFuture =
+ Futures.catchingAsync(
+ retrieveFuture,
+ IqErrorException.class,
+ ex -> {
+ final var error = ex.getError();
+ if (error != null
+ && error.getCondition() instanceof Condition.ItemNotFound) {
+ return Futures.immediateFuture(null);
+ } else {
+ return Futures.immediateFailedFuture(ex);
+ }
+ },
+ MoreExecutors.directExecutor());
+
+ return Futures.transformAsync(
+ caughtFuture,
+ existing -> {
+ final VCard vCard;
+ if (existing == null) {
+ Log.d(Config.LOGTAG, "item-not-found. created fresh vCard");
+ vCard = new VCard();
+ } else {
+ vCard = existing;
+ }
+ final var photo = new Photo();
+ photo.setType(type);
+ photo.addExtension(new BinaryValue()).setContent(image);
+ vCard.setExtension(photo);
+ return publish(address, vCard);
+ },
+ MoreExecutors.directExecutor());
+ }
}
@@ -59,6 +59,16 @@ public class Extension extends Element {
return child;
}
+ public <T extends Extension> T setExtension(T child) {
+ final var iterator = this.children.iterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().getClass().isInstance(child)) {
+ iterator.remove();
+ }
+ }
+ return this.addExtension(child);
+ }
+
public void addExtensions(final Collection<? extends Extension> extensions) {
for (final Extension extension : extensions) {
addExtension(extension);
@@ -12,4 +12,9 @@ public class Photo extends Extension {
public BinaryValue getBinaryValue() {
return this.getExtension(BinaryValue.class);
}
+
+ public void setType(final String value) {
+ final var type = this.addExtension(new Type());
+ type.setContent(value);
+ }
}
@@ -0,0 +1,12 @@
+package im.conversations.android.xmpp.model.vcard;
+
+import im.conversations.android.annotation.XmlElement;
+import im.conversations.android.xmpp.model.Extension;
+
+@XmlElement(name = "TYPE")
+public class Type extends Extension {
+
+ public Type() {
+ super(Type.class);
+ }
+}